summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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/gen_qfloat16_tables.cpp163
-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
509 files changed, 64240 insertions, 8831 deletions
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)
+{
+}
+
+VmaBlockMetadata_Linear::~VmaBlockMetadata_Linear()
+{
+}
+
+void VmaBlockMetadata_Linear::Init(VkDeviceSize size)
+{
+ VmaBlockMetadata::Init(size);
+ m_SumFreeSize = size;
+}
+
+bool VmaBlockMetadata_Linear::Validate() const
+{
+ const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
+ const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
+
+ VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
+ VMA_VALIDATE(!suballocations1st.empty() ||
+ suballocations2nd.empty() ||
+ m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
+
+ if(!suballocations1st.empty())
+ {
+ // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.
+ VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].hAllocation != VK_NULL_HANDLE);
+ // Null item at the end should be just pop_back().
+ VMA_VALIDATE(suballocations1st.back().hAllocation != VK_NULL_HANDLE);
+ }
+ if(!suballocations2nd.empty())
+ {
+ // Null item at the end should be just pop_back().
+ VMA_VALIDATE(suballocations2nd.back().hAllocation != VK_NULL_HANDLE);
+ }
+
+ VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
+ VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
+
+ VkDeviceSize sumUsedSize = 0;
+ const size_t suballoc1stCount = suballocations1st.size();
+ VkDeviceSize offset = VMA_DEBUG_MARGIN;
+
+ if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
+ {
+ const size_t suballoc2ndCount = suballocations2nd.size();
+ size_t nullItem2ndCount = 0;
+ for(size_t i = 0; i < suballoc2ndCount; ++i)
+ {
+ const VmaSuballocation& suballoc = suballocations2nd[i];
+ const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
+
+ VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
+ VMA_VALIDATE(suballoc.offset >= offset);
+
+ if(!currFree)
+ {
+ VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
+ VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
+ sumUsedSize += suballoc.size;
+ }
+ else
+ {
+ ++nullItem2ndCount;
+ }
+
+ offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
+ }
+
+ VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
+ }
+
+ for(size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
+ {
+ const VmaSuballocation& suballoc = suballocations1st[i];
+ VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&
+ suballoc.hAllocation == VK_NULL_HANDLE);
+ }
+
+ size_t nullItem1stCount = m_1stNullItemsBeginCount;
+
+ for(size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
+ {
+ const VmaSuballocation& suballoc = suballocations1st[i];
+ const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
+
+ VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
+ VMA_VALIDATE(suballoc.offset >= offset);
+ VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
+
+ if(!currFree)
+ {
+ VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
+ VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
+ sumUsedSize += suballoc.size;
+ }
+ else
+ {
+ ++nullItem1stCount;
+ }
+
+ offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
+ }
+ VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
+
+ if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
+ {
+ const size_t suballoc2ndCount = suballocations2nd.size();
+ size_t nullItem2ndCount = 0;
+ for(size_t i = suballoc2ndCount; i--; )
+ {
+ const VmaSuballocation& suballoc = suballocations2nd[i];
+ const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
+
+ VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
+ VMA_VALIDATE(suballoc.offset >= offset);
+
+ if(!currFree)
+ {
+ VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
+ VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
+ sumUsedSize += suballoc.size;
+ }
+ else
+ {
+ ++nullItem2ndCount;
+ }
+
+ offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
+ }
+
+ VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
+ }
+
+ VMA_VALIDATE(offset <= GetSize());
+ VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
+
+ return true;
+}
+
+size_t VmaBlockMetadata_Linear::GetAllocationCount() const
+{
+ return AccessSuballocations1st().size() - (m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount) +
+ AccessSuballocations2nd().size() - m_2ndNullItemsCount;
+}
+
+VkDeviceSize VmaBlockMetadata_Linear::GetUnusedRangeSizeMax() const
+{
+ const VkDeviceSize size = GetSize();
+
+ /*
+ We don't consider gaps inside allocation vectors with freed allocations because
+ they are not suitable for reuse in linear allocator. We consider only space that
+ is available for new allocations.
+ */
+ if(IsEmpty())
+ {
+ return size;
+ }
+
+ const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
+
+ switch(m_2ndVectorMode)
+ {
+ case SECOND_VECTOR_EMPTY:
+ /*
+ Available space is after end of 1st, as well as before beginning of 1st (which
+ whould make it a ring buffer).
+ */
+ {
+ const size_t suballocations1stCount = suballocations1st.size();
+ VMA_ASSERT(suballocations1stCount > m_1stNullItemsBeginCount);
+ const VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
+ const VmaSuballocation& lastSuballoc = suballocations1st[suballocations1stCount - 1];
+ return VMA_MAX(
+ firstSuballoc.offset,
+ size - (lastSuballoc.offset + lastSuballoc.size));
+ }
+ break;
+
+ case SECOND_VECTOR_RING_BUFFER:
+ /*
+ Available space is only between end of 2nd and beginning of 1st.
+ */
+ {
+ const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
+ const VmaSuballocation& lastSuballoc2nd = suballocations2nd.back();
+ const VmaSuballocation& firstSuballoc1st = suballocations1st[m_1stNullItemsBeginCount];
+ return firstSuballoc1st.offset - (lastSuballoc2nd.offset + lastSuballoc2nd.size);
+ }
+ break;
+
+ case SECOND_VECTOR_DOUBLE_STACK:
+ /*
+ Available space is only between end of 1st and top of 2nd.
+ */
+ {
+ const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
+ const VmaSuballocation& topSuballoc2nd = suballocations2nd.back();
+ const VmaSuballocation& lastSuballoc1st = suballocations1st.back();
+ return topSuballoc2nd.offset - (lastSuballoc1st.offset + lastSuballoc1st.size);
+ }
+ break;
+
+ default:
+ VMA_ASSERT(0);
+ return 0;
+ }
+}
+
+void VmaBlockMetadata_Linear::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
+{
+ const VkDeviceSize size = GetSize();
+ const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
+ const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
+ const size_t suballoc1stCount = suballocations1st.size();
+ const size_t suballoc2ndCount = suballocations2nd.size();
+
+ outInfo.blockCount = 1;
+ outInfo.allocationCount = (uint32_t)GetAllocationCount();
+ outInfo.unusedRangeCount = 0;
+ outInfo.usedBytes = 0;
+ outInfo.allocationSizeMin = UINT64_MAX;
+ outInfo.allocationSizeMax = 0;
+ outInfo.unusedRangeSizeMin = UINT64_MAX;
+ outInfo.unusedRangeSizeMax = 0;
+
+ VkDeviceSize lastOffset = 0;
+
+ if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
+ {
+ const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
+ size_t nextAlloc2ndIndex = 0;
+ while(lastOffset < freeSpace2ndTo1stEnd)
+ {
+ // Find next non-null allocation or move nextAllocIndex to the end.
+ while(nextAlloc2ndIndex < suballoc2ndCount &&
+ suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
+ {
+ ++nextAlloc2ndIndex;
+ }
+
+ // Found non-null allocation.
+ if(nextAlloc2ndIndex < suballoc2ndCount)
+ {
+ const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
+
+ // 1. Process free space before this allocation.
+ if(lastOffset < suballoc.offset)
+ {
+ // There is free space from lastOffset to suballoc.offset.
+ const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
+ ++outInfo.unusedRangeCount;
+ outInfo.unusedBytes += unusedRangeSize;
+ outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
+ outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
+ }
+
+ // 2. Process this allocation.
+ // There is allocation with suballoc.offset, suballoc.size.
+ outInfo.usedBytes += suballoc.size;
+ outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
+ outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
+
+ // 3. Prepare for next iteration.
+ lastOffset = suballoc.offset + suballoc.size;
+ ++nextAlloc2ndIndex;
+ }
+ // We are at the end.
+ else
+ {
+ // There is free space from lastOffset to freeSpace2ndTo1stEnd.
+ if(lastOffset < freeSpace2ndTo1stEnd)
+ {
+ const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
+ ++outInfo.unusedRangeCount;
+ outInfo.unusedBytes += unusedRangeSize;
+ outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
+ outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
+ }
+
+ // End of loop.
+ lastOffset = freeSpace2ndTo1stEnd;
+ }
+ }
+ }
+
+ size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
+ const VkDeviceSize freeSpace1stTo2ndEnd =
+ m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
+ while(lastOffset < freeSpace1stTo2ndEnd)
+ {
+ // Find next non-null allocation or move nextAllocIndex to the end.
+ while(nextAlloc1stIndex < suballoc1stCount &&
+ suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
+ {
+ ++nextAlloc1stIndex;
+ }
+
+ // Found non-null allocation.
+ if(nextAlloc1stIndex < suballoc1stCount)
+ {
+ const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
+
+ // 1. Process free space before this allocation.
+ if(lastOffset < suballoc.offset)
+ {
+ // There is free space from lastOffset to suballoc.offset.
+ const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
+ ++outInfo.unusedRangeCount;
+ outInfo.unusedBytes += unusedRangeSize;
+ outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
+ outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
+ }
+
+ // 2. Process this allocation.
+ // There is allocation with suballoc.offset, suballoc.size.
+ outInfo.usedBytes += suballoc.size;
+ outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
+ outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
+
+ // 3. Prepare for next iteration.
+ lastOffset = suballoc.offset + suballoc.size;
+ ++nextAlloc1stIndex;
+ }
+ // We are at the end.
+ else
+ {
+ // There is free space from lastOffset to freeSpace1stTo2ndEnd.
+ if(lastOffset < freeSpace1stTo2ndEnd)
+ {
+ const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
+ ++outInfo.unusedRangeCount;
+ outInfo.unusedBytes += unusedRangeSize;
+ outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
+ outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
+ }
+
+ // End of loop.
+ lastOffset = freeSpace1stTo2ndEnd;
+ }
+ }
+
+ if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
+ {
+ size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
+ while(lastOffset < size)
+ {
+ // Find next non-null allocation or move nextAllocIndex to the end.
+ while(nextAlloc2ndIndex != SIZE_MAX &&
+ suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
+ {
+ --nextAlloc2ndIndex;
+ }
+
+ // Found non-null allocation.
+ if(nextAlloc2ndIndex != SIZE_MAX)
+ {
+ const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
+
+ // 1. Process free space before this allocation.
+ if(lastOffset < suballoc.offset)
+ {
+ // There is free space from lastOffset to suballoc.offset.
+ const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
+ ++outInfo.unusedRangeCount;
+ outInfo.unusedBytes += unusedRangeSize;
+ outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
+ outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
+ }
+
+ // 2. Process this allocation.
+ // There is allocation with suballoc.offset, suballoc.size.
+ outInfo.usedBytes += suballoc.size;
+ outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
+ outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
+
+ // 3. Prepare for next iteration.
+ lastOffset = suballoc.offset + suballoc.size;
+ --nextAlloc2ndIndex;
+ }
+ // We are at the end.
+ else
+ {
+ // There is free space from lastOffset to size.
+ if(lastOffset < size)
+ {
+ const VkDeviceSize unusedRangeSize = size - lastOffset;
+ ++outInfo.unusedRangeCount;
+ outInfo.unusedBytes += unusedRangeSize;
+ outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
+ outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
+ }
+
+ // End of loop.
+ lastOffset = size;
+ }
+ }
+ }
+
+ outInfo.unusedBytes = size - outInfo.usedBytes;
+}
+
+void VmaBlockMetadata_Linear::AddPoolStats(VmaPoolStats& inoutStats) const
+{
+ const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
+ const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
+ const VkDeviceSize size = GetSize();
+ const size_t suballoc1stCount = suballocations1st.size();
+ const size_t suballoc2ndCount = suballocations2nd.size();
+
+ inoutStats.size += size;
+
+ VkDeviceSize lastOffset = 0;
+
+ if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
+ {
+ const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
+ size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;
+ while(lastOffset < freeSpace2ndTo1stEnd)
+ {
+ // Find next non-null allocation or move nextAlloc2ndIndex to the end.
+ while(nextAlloc2ndIndex < suballoc2ndCount &&
+ suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
+ {
+ ++nextAlloc2ndIndex;
+ }
+
+ // Found non-null allocation.
+ if(nextAlloc2ndIndex < suballoc2ndCount)
+ {
+ const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
+
+ // 1. Process free space before this allocation.
+ if(lastOffset < suballoc.offset)
+ {
+ // There is free space from lastOffset to suballoc.offset.
+ const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
+ inoutStats.unusedSize += unusedRangeSize;
+ ++inoutStats.unusedRangeCount;
+ inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
+ }
+
+ // 2. Process this allocation.
+ // There is allocation with suballoc.offset, suballoc.size.
+ ++inoutStats.allocationCount;
+
+ // 3. Prepare for next iteration.
+ lastOffset = suballoc.offset + suballoc.size;
+ ++nextAlloc2ndIndex;
+ }
+ // We are at the end.
+ else
+ {
+ if(lastOffset < freeSpace2ndTo1stEnd)
+ {
+ // There is free space from lastOffset to freeSpace2ndTo1stEnd.
+ const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
+ inoutStats.unusedSize += unusedRangeSize;
+ ++inoutStats.unusedRangeCount;
+ inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
+ }
+
+ // End of loop.
+ lastOffset = freeSpace2ndTo1stEnd;
+ }
+ }
+ }
+
+ size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
+ const VkDeviceSize freeSpace1stTo2ndEnd =
+ m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
+ while(lastOffset < freeSpace1stTo2ndEnd)
+ {
+ // Find next non-null allocation or move nextAllocIndex to the end.
+ while(nextAlloc1stIndex < suballoc1stCount &&
+ suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
+ {
+ ++nextAlloc1stIndex;
+ }
+
+ // Found non-null allocation.
+ if(nextAlloc1stIndex < suballoc1stCount)
+ {
+ const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
+
+ // 1. Process free space before this allocation.
+ if(lastOffset < suballoc.offset)
+ {
+ // There is free space from lastOffset to suballoc.offset.
+ const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
+ inoutStats.unusedSize += unusedRangeSize;
+ ++inoutStats.unusedRangeCount;
+ inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
+ }
+
+ // 2. Process this allocation.
+ // There is allocation with suballoc.offset, suballoc.size.
+ ++inoutStats.allocationCount;
+
+ // 3. Prepare for next iteration.
+ lastOffset = suballoc.offset + suballoc.size;
+ ++nextAlloc1stIndex;
+ }
+ // We are at the end.
+ else
+ {
+ if(lastOffset < freeSpace1stTo2ndEnd)
+ {
+ // There is free space from lastOffset to freeSpace1stTo2ndEnd.
+ const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
+ inoutStats.unusedSize += unusedRangeSize;
+ ++inoutStats.unusedRangeCount;
+ inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
+ }
+
+ // End of loop.
+ lastOffset = freeSpace1stTo2ndEnd;
+ }
+ }
+
+ if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
+ {
+ size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
+ while(lastOffset < size)
+ {
+ // Find next non-null allocation or move nextAlloc2ndIndex to the end.
+ while(nextAlloc2ndIndex != SIZE_MAX &&
+ suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
+ {
+ --nextAlloc2ndIndex;
+ }
+
+ // Found non-null allocation.
+ if(nextAlloc2ndIndex != SIZE_MAX)
+ {
+ const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
+
+ // 1. Process free space before this allocation.
+ if(lastOffset < suballoc.offset)
+ {
+ // There is free space from lastOffset to suballoc.offset.
+ const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
+ inoutStats.unusedSize += unusedRangeSize;
+ ++inoutStats.unusedRangeCount;
+ inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
+ }
+
+ // 2. Process this allocation.
+ // There is allocation with suballoc.offset, suballoc.size.
+ ++inoutStats.allocationCount;
+
+ // 3. Prepare for next iteration.
+ lastOffset = suballoc.offset + suballoc.size;
+ --nextAlloc2ndIndex;
+ }
+ // We are at the end.
+ else
+ {
+ if(lastOffset < size)
+ {
+ // There is free space from lastOffset to size.
+ const VkDeviceSize unusedRangeSize = size - lastOffset;
+ inoutStats.unusedSize += unusedRangeSize;
+ ++inoutStats.unusedRangeCount;
+ inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
+ }
+
+ // End of loop.
+ lastOffset = size;
+ }
+ }
+ }
+}
+
+#if VMA_STATS_STRING_ENABLED
+void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
+{
+ const VkDeviceSize size = GetSize();
+ const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
+ const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
+ const size_t suballoc1stCount = suballocations1st.size();
+ const size_t suballoc2ndCount = suballocations2nd.size();
+
+ // FIRST PASS
+
+ size_t unusedRangeCount = 0;
+ VkDeviceSize usedBytes = 0;
+
+ VkDeviceSize lastOffset = 0;
+
+ size_t alloc2ndCount = 0;
+ if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
+ {
+ const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
+ size_t nextAlloc2ndIndex = 0;
+ while(lastOffset < freeSpace2ndTo1stEnd)
+ {
+ // Find next non-null allocation or move nextAlloc2ndIndex to the end.
+ while(nextAlloc2ndIndex < suballoc2ndCount &&
+ suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
+ {
+ ++nextAlloc2ndIndex;
+ }
+
+ // Found non-null allocation.
+ if(nextAlloc2ndIndex < suballoc2ndCount)
+ {
+ const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
+
+ // 1. Process free space before this allocation.
+ if(lastOffset < suballoc.offset)
+ {
+ // There is free space from lastOffset to suballoc.offset.
+ ++unusedRangeCount;
+ }
+
+ // 2. Process this allocation.
+ // There is allocation with suballoc.offset, suballoc.size.
+ ++alloc2ndCount;
+ usedBytes += suballoc.size;
+
+ // 3. Prepare for next iteration.
+ lastOffset = suballoc.offset + suballoc.size;
+ ++nextAlloc2ndIndex;
+ }
+ // We are at the end.
+ else
+ {
+ if(lastOffset < freeSpace2ndTo1stEnd)
+ {
+ // There is free space from lastOffset to freeSpace2ndTo1stEnd.
+ ++unusedRangeCount;
+ }
+
+ // End of loop.
+ lastOffset = freeSpace2ndTo1stEnd;
+ }
+ }
+ }
+
+ size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
+ size_t alloc1stCount = 0;
+ const VkDeviceSize freeSpace1stTo2ndEnd =
+ m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
+ while(lastOffset < freeSpace1stTo2ndEnd)
+ {
+ // Find next non-null allocation or move nextAllocIndex to the end.
+ while(nextAlloc1stIndex < suballoc1stCount &&
+ suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
+ {
+ ++nextAlloc1stIndex;
+ }
+
+ // Found non-null allocation.
+ if(nextAlloc1stIndex < suballoc1stCount)
+ {
+ const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
+
+ // 1. Process free space before this allocation.
+ if(lastOffset < suballoc.offset)
+ {
+ // There is free space from lastOffset to suballoc.offset.
+ ++unusedRangeCount;
+ }
+
+ // 2. Process this allocation.
+ // There is allocation with suballoc.offset, suballoc.size.
+ ++alloc1stCount;
+ usedBytes += suballoc.size;
+
+ // 3. Prepare for next iteration.
+ lastOffset = suballoc.offset + suballoc.size;
+ ++nextAlloc1stIndex;
+ }
+ // We are at the end.
+ else
+ {
+ if(lastOffset < size)
+ {
+ // There is free space from lastOffset to freeSpace1stTo2ndEnd.
+ ++unusedRangeCount;
+ }
+
+ // End of loop.
+ lastOffset = freeSpace1stTo2ndEnd;
+ }
+ }
+
+ if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
+ {
+ size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
+ while(lastOffset < size)
+ {
+ // Find next non-null allocation or move nextAlloc2ndIndex to the end.
+ while(nextAlloc2ndIndex != SIZE_MAX &&
+ suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
+ {
+ --nextAlloc2ndIndex;
+ }
+
+ // Found non-null allocation.
+ if(nextAlloc2ndIndex != SIZE_MAX)
+ {
+ const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
+
+ // 1. Process free space before this allocation.
+ if(lastOffset < suballoc.offset)
+ {
+ // There is free space from lastOffset to suballoc.offset.
+ ++unusedRangeCount;
+ }
+
+ // 2. Process this allocation.
+ // There is allocation with suballoc.offset, suballoc.size.
+ ++alloc2ndCount;
+ usedBytes += suballoc.size;
+
+ // 3. Prepare for next iteration.
+ lastOffset = suballoc.offset + suballoc.size;
+ --nextAlloc2ndIndex;
+ }
+ // We are at the end.
+ else
+ {
+ if(lastOffset < size)
+ {
+ // There is free space from lastOffset to size.
+ ++unusedRangeCount;
+ }
+
+ // End of loop.
+ lastOffset = size;
+ }
+ }
+ }
+
+ const VkDeviceSize unusedBytes = size - usedBytes;
+ PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
+
+ // SECOND PASS
+ lastOffset = 0;
+
+ if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
+ {
+ const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
+ size_t nextAlloc2ndIndex = 0;
+ while(lastOffset < freeSpace2ndTo1stEnd)
+ {
+ // Find next non-null allocation or move nextAlloc2ndIndex to the end.
+ while(nextAlloc2ndIndex < suballoc2ndCount &&
+ suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
+ {
+ ++nextAlloc2ndIndex;
+ }
+
+ // Found non-null allocation.
+ if(nextAlloc2ndIndex < suballoc2ndCount)
+ {
+ const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
+
+ // 1. Process free space before this allocation.
+ if(lastOffset < suballoc.offset)
+ {
+ // There is free space from lastOffset to suballoc.offset.
+ const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
+ PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
+ }
+
+ // 2. Process this allocation.
+ // There is allocation with suballoc.offset, suballoc.size.
+ PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
+
+ // 3. Prepare for next iteration.
+ lastOffset = suballoc.offset + suballoc.size;
+ ++nextAlloc2ndIndex;
+ }
+ // We are at the end.
+ else
+ {
+ if(lastOffset < freeSpace2ndTo1stEnd)
+ {
+ // There is free space from lastOffset to freeSpace2ndTo1stEnd.
+ const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
+ PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
+ }
+
+ // End of loop.
+ lastOffset = freeSpace2ndTo1stEnd;
+ }
+ }
+ }
+
+ nextAlloc1stIndex = m_1stNullItemsBeginCount;
+ while(lastOffset < freeSpace1stTo2ndEnd)
+ {
+ // Find next non-null allocation or move nextAllocIndex to the end.
+ while(nextAlloc1stIndex < suballoc1stCount &&
+ suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
+ {
+ ++nextAlloc1stIndex;
+ }
+
+ // Found non-null allocation.
+ if(nextAlloc1stIndex < suballoc1stCount)
+ {
+ const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
+
+ // 1. Process free space before this allocation.
+ if(lastOffset < suballoc.offset)
+ {
+ // There is free space from lastOffset to suballoc.offset.
+ const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
+ PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
+ }
+
+ // 2. Process this allocation.
+ // There is allocation with suballoc.offset, suballoc.size.
+ PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
+
+ // 3. Prepare for next iteration.
+ lastOffset = suballoc.offset + suballoc.size;
+ ++nextAlloc1stIndex;
+ }
+ // We are at the end.
+ else
+ {
+ if(lastOffset < freeSpace1stTo2ndEnd)
+ {
+ // There is free space from lastOffset to freeSpace1stTo2ndEnd.
+ const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
+ PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
+ }
+
+ // End of loop.
+ lastOffset = freeSpace1stTo2ndEnd;
+ }
+ }
+
+ if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
+ {
+ size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
+ while(lastOffset < size)
+ {
+ // Find next non-null allocation or move nextAlloc2ndIndex to the end.
+ while(nextAlloc2ndIndex != SIZE_MAX &&
+ suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
+ {
+ --nextAlloc2ndIndex;
+ }
+
+ // Found non-null allocation.
+ if(nextAlloc2ndIndex != SIZE_MAX)
+ {
+ const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
+
+ // 1. Process free space before this allocation.
+ if(lastOffset < suballoc.offset)
+ {
+ // There is free space from lastOffset to suballoc.offset.
+ const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
+ PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
+ }
+
+ // 2. Process this allocation.
+ // There is allocation with suballoc.offset, suballoc.size.
+ PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
+
+ // 3. Prepare for next iteration.
+ lastOffset = suballoc.offset + suballoc.size;
+ --nextAlloc2ndIndex;
+ }
+ // We are at the end.
+ else
+ {
+ if(lastOffset < size)
+ {
+ // There is free space from lastOffset to size.
+ const VkDeviceSize unusedRangeSize = size - lastOffset;
+ PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
+ }
+
+ // End of loop.
+ lastOffset = size;
+ }
+ }
+ }
+
+ PrintDetailedMap_End(json);
+}
+#endif // #if VMA_STATS_STRING_ENABLED
+
+bool VmaBlockMetadata_Linear::CreateAllocationRequest(
+ uint32_t currentFrameIndex,
+ uint32_t frameInUseCount,
+ VkDeviceSize bufferImageGranularity,
+ VkDeviceSize allocSize,
+ VkDeviceSize allocAlignment,
+ bool upperAddress,
+ VmaSuballocationType allocType,
+ bool canMakeOtherLost,
+ uint32_t /*strategy*/,
+ VmaAllocationRequest* pAllocationRequest)
+{
+ VMA_ASSERT(allocSize > 0);
+ VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
+ VMA_ASSERT(pAllocationRequest != VMA_NULL);
+ VMA_HEAVY_ASSERT(Validate());
+
+ const VkDeviceSize size = GetSize();
+ SuballocationVectorType& suballocations1st = AccessSuballocations1st();
+ SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
+
+ if(upperAddress)
+ {
+ if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
+ {
+ VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
+ return false;
+ }
+
+ // Try to allocate before 2nd.back(), or end of block if 2nd.empty().
+ if(allocSize > size)
+ {
+ return false;
+ }
+ VkDeviceSize resultBaseOffset = size - allocSize;
+ if(!suballocations2nd.empty())
+ {
+ const VmaSuballocation& lastSuballoc = suballocations2nd.back();
+ resultBaseOffset = lastSuballoc.offset - allocSize;
+ if(allocSize > lastSuballoc.offset)
+ {
+ return false;
+ }
+ }
+
+ // Start from offset equal to end of free space.
+ VkDeviceSize resultOffset = resultBaseOffset;
+
+ // Apply VMA_DEBUG_MARGIN at the end.
+ if(VMA_DEBUG_MARGIN > 0)
+ {
+#if VMA_DEBUG_MARGIN
+ if(resultOffset < VMA_DEBUG_MARGIN)
+ {
+ return false;
+ }
+#endif
+ resultOffset -= VMA_DEBUG_MARGIN;
+ }
+
+ // Apply alignment.
+ resultOffset = VmaAlignDown(resultOffset, allocAlignment);
+
+ // Check next suballocations from 2nd for BufferImageGranularity conflicts.
+ // Make bigger alignment if necessary.
+ if(bufferImageGranularity > 1 && !suballocations2nd.empty())
+ {
+ bool bufferImageGranularityConflict = false;
+ for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
+ {
+ const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
+ if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
+ {
+ if(VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))
+ {
+ bufferImageGranularityConflict = true;
+ break;
+ }
+ }
+ else
+ // Already on previous page.
+ break;
+ }
+ if(bufferImageGranularityConflict)
+ {
+ resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);
+ }
+ }
+
+ // There is enough free space.
+ const VkDeviceSize endOf1st = !suballocations1st.empty() ?
+ suballocations1st.back().offset + suballocations1st.back().size :
+ 0;
+ if(endOf1st + VMA_DEBUG_MARGIN <= resultOffset)
+ {
+ // Check previous suballocations for BufferImageGranularity conflicts.
+ // If conflict exists, allocation cannot be made here.
+ if(bufferImageGranularity > 1)
+ {
+ for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
+ {
+ const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
+ if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
+ {
+ if(VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ // Already on next page.
+ break;
+ }
+ }
+ }
+
+ // All tests passed: Success.
+ pAllocationRequest->offset = resultOffset;
+ pAllocationRequest->sumFreeSize = resultBaseOffset + allocSize - endOf1st;
+ pAllocationRequest->sumItemSize = 0;
+ // pAllocationRequest->item unused.
+ pAllocationRequest->itemsToMakeLostCount = 0;
+ return true;
+ }
+ }
+ else // !upperAddress
+ {
+ if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
+ {
+ // Try to allocate at the end of 1st vector.
+
+ VkDeviceSize resultBaseOffset = 0;
+ if(!suballocations1st.empty())
+ {
+ const VmaSuballocation& lastSuballoc = suballocations1st.back();
+ resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
+ }
+
+ // Start from offset equal to beginning of free space.
+ VkDeviceSize resultOffset = resultBaseOffset;
+
+ // Apply VMA_DEBUG_MARGIN at the beginning.
+ if(VMA_DEBUG_MARGIN > 0)
+ {
+ resultOffset += VMA_DEBUG_MARGIN;
+ }
+
+ // Apply alignment.
+ resultOffset = VmaAlignUp(resultOffset, allocAlignment);
+
+ // Check previous suballocations for BufferImageGranularity conflicts.
+ // Make bigger alignment if necessary.
+ if(bufferImageGranularity > 1 && !suballocations1st.empty())
+ {
+ bool bufferImageGranularityConflict = false;
+ for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
+ {
+ const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
+ if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
+ {
+ if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
+ {
+ bufferImageGranularityConflict = true;
+ break;
+ }
+ }
+ else
+ // Already on previous page.
+ break;
+ }
+ if(bufferImageGranularityConflict)
+ {
+ resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
+ }
+ }
+
+ const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
+ suballocations2nd.back().offset : size;
+
+ // There is enough free space at the end after alignment.
+ if(resultOffset + allocSize + VMA_DEBUG_MARGIN <= freeSpaceEnd)
+ {
+ // Check next suballocations for BufferImageGranularity conflicts.
+ // If conflict exists, allocation cannot be made here.
+ if(bufferImageGranularity > 1 && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
+ {
+ for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
+ {
+ const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
+ if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
+ {
+ if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ // Already on previous page.
+ break;
+ }
+ }
+ }
+
+ // All tests passed: Success.
+ pAllocationRequest->offset = resultOffset;
+ pAllocationRequest->sumFreeSize = freeSpaceEnd - resultBaseOffset;
+ pAllocationRequest->sumItemSize = 0;
+ // pAllocationRequest->item unused.
+ pAllocationRequest->itemsToMakeLostCount = 0;
+ return true;
+ }
+ }
+
+ // Wrap-around to end of 2nd vector. Try to allocate there, watching for the
+ // beginning of 1st vector as the end of free space.
+ if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
+ {
+ VMA_ASSERT(!suballocations1st.empty());
+
+ VkDeviceSize resultBaseOffset = 0;
+ if(!suballocations2nd.empty())
+ {
+ const VmaSuballocation& lastSuballoc = suballocations2nd.back();
+ resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
+ }
+
+ // Start from offset equal to beginning of free space.
+ VkDeviceSize resultOffset = resultBaseOffset;
+
+ // Apply VMA_DEBUG_MARGIN at the beginning.
+ if(VMA_DEBUG_MARGIN > 0)
+ {
+ resultOffset += VMA_DEBUG_MARGIN;
+ }
+
+ // Apply alignment.
+ resultOffset = VmaAlignUp(resultOffset, allocAlignment);
+
+ // Check previous suballocations for BufferImageGranularity conflicts.
+ // Make bigger alignment if necessary.
+ if(bufferImageGranularity > 1 && !suballocations2nd.empty())
+ {
+ bool bufferImageGranularityConflict = false;
+ for(size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )
+ {
+ const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];
+ if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
+ {
+ if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
+ {
+ bufferImageGranularityConflict = true;
+ break;
+ }
+ }
+ else
+ // Already on previous page.
+ break;
+ }
+ if(bufferImageGranularityConflict)
+ {
+ resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
+ }
+ }
+
+ pAllocationRequest->itemsToMakeLostCount = 0;
+ pAllocationRequest->sumItemSize = 0;
+ size_t index1st = m_1stNullItemsBeginCount;
+
+ if(canMakeOtherLost)
+ {
+ while(index1st < suballocations1st.size() &&
+ resultOffset + allocSize + VMA_DEBUG_MARGIN > suballocations1st[index1st].offset)
+ {
+ // Next colliding allocation at the beginning of 1st vector found. Try to make it lost.
+ const VmaSuballocation& suballoc = suballocations1st[index1st];
+ if(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ // No problem.
+ }
+ else
+ {
+ VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
+ if(suballoc.hAllocation->CanBecomeLost() &&
+ suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
+ {
+ ++pAllocationRequest->itemsToMakeLostCount;
+ pAllocationRequest->sumItemSize += suballoc.size;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ ++index1st;
+ }
+
+ // Check next suballocations for BufferImageGranularity conflicts.
+ // If conflict exists, we must mark more allocations lost or fail.
+ if(bufferImageGranularity > 1)
+ {
+ while(index1st < suballocations1st.size())
+ {
+ const VmaSuballocation& suballoc = suballocations1st[index1st];
+ if(VmaBlocksOnSamePage(resultOffset, allocSize, suballoc.offset, bufferImageGranularity))
+ {
+ if(suballoc.hAllocation != VK_NULL_HANDLE)
+ {
+ // Not checking actual VmaIsBufferImageGranularityConflict(allocType, suballoc.type).
+ if(suballoc.hAllocation->CanBecomeLost() &&
+ suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
+ {
+ ++pAllocationRequest->itemsToMakeLostCount;
+ pAllocationRequest->sumItemSize += suballoc.size;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ // Already on next page.
+ break;
+ }
+ ++index1st;
+ }
+ }
+ }
+
+ // There is enough free space at the end after alignment.
+ if((index1st == suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN < size) ||
+ (index1st < suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= suballocations1st[index1st].offset))
+ {
+ // Check next suballocations for BufferImageGranularity conflicts.
+ // If conflict exists, allocation cannot be made here.
+ if(bufferImageGranularity > 1)
+ {
+ for(size_t nextSuballocIndex = index1st;
+ nextSuballocIndex < suballocations1st.size();
+ nextSuballocIndex++)
+ {
+ const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];
+ if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
+ {
+ if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ // Already on next page.
+ break;
+ }
+ }
+ }
+
+ // All tests passed: Success.
+ pAllocationRequest->offset = resultOffset;
+ pAllocationRequest->sumFreeSize =
+ (index1st < suballocations1st.size() ? suballocations1st[index1st].offset : size)
+ - resultBaseOffset
+ - pAllocationRequest->sumItemSize;
+ // pAllocationRequest->item unused.
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool VmaBlockMetadata_Linear::MakeRequestedAllocationsLost(
+ uint32_t currentFrameIndex,
+ uint32_t frameInUseCount,
+ VmaAllocationRequest* pAllocationRequest)
+{
+ if(pAllocationRequest->itemsToMakeLostCount == 0)
+ {
+ return true;
+ }
+
+ VMA_ASSERT(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER);
+
+ SuballocationVectorType& suballocations1st = AccessSuballocations1st();
+ size_t index1st = m_1stNullItemsBeginCount;
+ size_t madeLostCount = 0;
+ while(madeLostCount < pAllocationRequest->itemsToMakeLostCount)
+ {
+ VMA_ASSERT(index1st < suballocations1st.size());
+ VmaSuballocation& suballoc = suballocations1st[index1st];
+ if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
+ VMA_ASSERT(suballoc.hAllocation->CanBecomeLost());
+ if(suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
+ {
+ suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
+ suballoc.hAllocation = VK_NULL_HANDLE;
+ m_SumFreeSize += suballoc.size;
+ ++m_1stNullItemsMiddleCount;
+ ++madeLostCount;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ ++index1st;
+ }
+
+ CleanupAfterFree();
+ //VMA_HEAVY_ASSERT(Validate()); // Already called by ClanupAfterFree().
+
+ return true;
+}
+
+uint32_t VmaBlockMetadata_Linear::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
+{
+ uint32_t lostAllocationCount = 0;
+
+ SuballocationVectorType& suballocations1st = AccessSuballocations1st();
+ for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
+ {
+ VmaSuballocation& suballoc = suballocations1st[i];
+ if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
+ suballoc.hAllocation->CanBecomeLost() &&
+ suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
+ {
+ suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
+ suballoc.hAllocation = VK_NULL_HANDLE;
+ ++m_1stNullItemsMiddleCount;
+ m_SumFreeSize += suballoc.size;
+ ++lostAllocationCount;
+ }
+ }
+
+ SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
+ for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
+ {
+ VmaSuballocation& suballoc = suballocations2nd[i];
+ if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
+ suballoc.hAllocation->CanBecomeLost() &&
+ suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
+ {
+ suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
+ suballoc.hAllocation = VK_NULL_HANDLE;
+ ++m_2ndNullItemsCount;
+ ++lostAllocationCount;
+ }
+ }
+
+ if(lostAllocationCount)
+ {
+ CleanupAfterFree();
+ }
+
+ return lostAllocationCount;
+}
+
+VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)
+{
+ SuballocationVectorType& suballocations1st = AccessSuballocations1st();
+ for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
+ {
+ const VmaSuballocation& suballoc = suballocations1st[i];
+ if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
+ {
+ VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
+ return VK_ERROR_VALIDATION_FAILED_EXT;
+ }
+ if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
+ {
+ VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
+ return VK_ERROR_VALIDATION_FAILED_EXT;
+ }
+ }
+ }
+
+ SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
+ for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
+ {
+ const VmaSuballocation& suballoc = suballocations2nd[i];
+ if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
+ {
+ VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
+ return VK_ERROR_VALIDATION_FAILED_EXT;
+ }
+ if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
+ {
+ VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
+ return VK_ERROR_VALIDATION_FAILED_EXT;
+ }
+ }
+ }
+
+ return VK_SUCCESS;
+}
+
+void VmaBlockMetadata_Linear::Alloc(
+ const VmaAllocationRequest& request,
+ VmaSuballocationType type,
+ VkDeviceSize allocSize,
+ bool upperAddress,
+ VmaAllocation hAllocation)
+{
+ const VmaSuballocation newSuballoc = { request.offset, allocSize, hAllocation, type };
+
+ if(upperAddress)
+ {
+ VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
+ "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
+ SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
+ suballocations2nd.push_back(newSuballoc);
+ m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
+ }
+ else
+ {
+ SuballocationVectorType& suballocations1st = AccessSuballocations1st();
+
+ // First allocation.
+ if(suballocations1st.empty())
+ {
+ suballocations1st.push_back(newSuballoc);
+ }
+ else
+ {
+ // New allocation at the end of 1st vector.
+ if(request.offset >= suballocations1st.back().offset + suballocations1st.back().size)
+ {
+ // Check if it fits before the end of the block.
+ VMA_ASSERT(request.offset + allocSize <= GetSize());
+ suballocations1st.push_back(newSuballoc);
+ }
+ // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.
+ else if(request.offset + allocSize <= suballocations1st[m_1stNullItemsBeginCount].offset)
+ {
+ SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
+
+ switch(m_2ndVectorMode)
+ {
+ case SECOND_VECTOR_EMPTY:
+ // First allocation from second part ring buffer.
+ VMA_ASSERT(suballocations2nd.empty());
+ m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
+ break;
+ case SECOND_VECTOR_RING_BUFFER:
+ // 2-part ring buffer is already started.
+ VMA_ASSERT(!suballocations2nd.empty());
+ break;
+ case SECOND_VECTOR_DOUBLE_STACK:
+ VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
+ break;
+ default:
+ VMA_ASSERT(0);
+ }
+
+ suballocations2nd.push_back(newSuballoc);
+ }
+ else
+ {
+ VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
+ }
+ }
+ }
+
+ m_SumFreeSize -= newSuballoc.size;
+}
+
+void VmaBlockMetadata_Linear::Free(const VmaAllocation allocation)
+{
+ FreeAtOffset(allocation->GetOffset());
+}
+
+void VmaBlockMetadata_Linear::FreeAtOffset(VkDeviceSize offset)
+{
+ SuballocationVectorType& suballocations1st = AccessSuballocations1st();
+ SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
+
+ if(!suballocations1st.empty())
+ {
+ // First allocation: Mark it as next empty at the beginning.
+ VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
+ if(firstSuballoc.offset == offset)
+ {
+ firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
+ firstSuballoc.hAllocation = VK_NULL_HANDLE;
+ m_SumFreeSize += firstSuballoc.size;
+ ++m_1stNullItemsBeginCount;
+ CleanupAfterFree();
+ return;
+ }
+ }
+
+ // Last allocation in 2-part ring buffer or top of upper stack (same logic).
+ if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
+ m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
+ {
+ VmaSuballocation& lastSuballoc = suballocations2nd.back();
+ if(lastSuballoc.offset == offset)
+ {
+ m_SumFreeSize += lastSuballoc.size;
+ suballocations2nd.pop_back();
+ CleanupAfterFree();
+ return;
+ }
+ }
+ // Last allocation in 1st vector.
+ else if(m_2ndVectorMode == SECOND_VECTOR_EMPTY)
+ {
+ VmaSuballocation& lastSuballoc = suballocations1st.back();
+ if(lastSuballoc.offset == offset)
+ {
+ m_SumFreeSize += lastSuballoc.size;
+ suballocations1st.pop_back();
+ CleanupAfterFree();
+ return;
+ }
+ }
+
+ // Item from the middle of 1st vector.
+ {
+ VmaSuballocation refSuballoc;
+ refSuballoc.offset = offset;
+ // Rest of members stays uninitialized intentionally for better performance.
+ SuballocationVectorType::iterator it = VmaVectorFindSorted<VmaSuballocationOffsetLess>(
+ suballocations1st.begin() + m_1stNullItemsBeginCount,
+ suballocations1st.end(),
+ refSuballoc);
+ if(it != suballocations1st.end())
+ {
+ it->type = VMA_SUBALLOCATION_TYPE_FREE;
+ it->hAllocation = VK_NULL_HANDLE;
+ ++m_1stNullItemsMiddleCount;
+ m_SumFreeSize += it->size;
+ CleanupAfterFree();
+ return;
+ }
+ }
+
+ if(m_2ndVectorMode != SECOND_VECTOR_EMPTY)
+ {
+ // Item from the middle of 2nd vector.
+ VmaSuballocation refSuballoc;
+ refSuballoc.offset = offset;
+ // Rest of members stays uninitialized intentionally for better performance.
+ SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
+ VmaVectorFindSorted<VmaSuballocationOffsetLess>(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc) :
+ VmaVectorFindSorted<VmaSuballocationOffsetGreater>(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc);
+ if(it != suballocations2nd.end())
+ {
+ it->type = VMA_SUBALLOCATION_TYPE_FREE;
+ it->hAllocation = VK_NULL_HANDLE;
+ ++m_2ndNullItemsCount;
+ m_SumFreeSize += it->size;
+ CleanupAfterFree();
+ return;
+ }
+ }
+
+ VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");
+}
+
+bool VmaBlockMetadata_Linear::ShouldCompact1st() const
+{
+ const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
+ const size_t suballocCount = AccessSuballocations1st().size();
+ return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
+}
+
+void VmaBlockMetadata_Linear::CleanupAfterFree()
+{
+ SuballocationVectorType& suballocations1st = AccessSuballocations1st();
+ SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
+
+ if(IsEmpty())
+ {
+ suballocations1st.clear();
+ suballocations2nd.clear();
+ m_1stNullItemsBeginCount = 0;
+ m_1stNullItemsMiddleCount = 0;
+ m_2ndNullItemsCount = 0;
+ m_2ndVectorMode = SECOND_VECTOR_EMPTY;
+ }
+ else
+ {
+ const size_t suballoc1stCount = suballocations1st.size();
+ const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
+ VMA_ASSERT(nullItem1stCount <= suballoc1stCount);
+
+ // Find more null items at the beginning of 1st vector.
+ while(m_1stNullItemsBeginCount < suballoc1stCount &&
+ suballocations1st[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
+ {
+ ++m_1stNullItemsBeginCount;
+ --m_1stNullItemsMiddleCount;
+ }
+
+ // Find more null items at the end of 1st vector.
+ while(m_1stNullItemsMiddleCount > 0 &&
+ suballocations1st.back().hAllocation == VK_NULL_HANDLE)
+ {
+ --m_1stNullItemsMiddleCount;
+ suballocations1st.pop_back();
+ }
+
+ // Find more null items at the end of 2nd vector.
+ while(m_2ndNullItemsCount > 0 &&
+ suballocations2nd.back().hAllocation == VK_NULL_HANDLE)
+ {
+ --m_2ndNullItemsCount;
+ suballocations2nd.pop_back();
+ }
+
+ if(ShouldCompact1st())
+ {
+ const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
+ size_t srcIndex = m_1stNullItemsBeginCount;
+ for(size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
+ {
+ while(suballocations1st[srcIndex].hAllocation == VK_NULL_HANDLE)
+ {
+ ++srcIndex;
+ }
+ if(dstIndex != srcIndex)
+ {
+ suballocations1st[dstIndex] = suballocations1st[srcIndex];
+ }
+ ++srcIndex;
+ }
+ suballocations1st.resize(nonNullItemCount);
+ m_1stNullItemsBeginCount = 0;
+ m_1stNullItemsMiddleCount = 0;
+ }
+
+ // 2nd vector became empty.
+ if(suballocations2nd.empty())
+ {
+ m_2ndVectorMode = SECOND_VECTOR_EMPTY;
+ }
+
+ // 1st vector became empty.
+ if(suballocations1st.size() - m_1stNullItemsBeginCount == 0)
+ {
+ suballocations1st.clear();
+ m_1stNullItemsBeginCount = 0;
+
+ if(!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
+ {
+ // Swap 1st with 2nd. Now 2nd is empty.
+ m_2ndVectorMode = SECOND_VECTOR_EMPTY;
+ m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
+ while(m_1stNullItemsBeginCount < suballocations2nd.size() &&
+ suballocations2nd[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
+ {
+ ++m_1stNullItemsBeginCount;
+ --m_1stNullItemsMiddleCount;
+ }
+ m_2ndNullItemsCount = 0;
+ m_1stVectorIndex ^= 1;
+ }
+ }
+ }
+
+ VMA_HEAVY_ASSERT(Validate());
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// class VmaBlockMetadata_Buddy
+
+VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(VmaAllocator hAllocator) :
+ VmaBlockMetadata(hAllocator),
+ m_Root(VMA_NULL),
+ m_AllocationCount(0),
+ m_FreeCount(1),
+ m_SumFreeSize(0)
+{
+ memset(m_FreeList, 0, sizeof(m_FreeList));
+}
+
+VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy()
+{
+ DeleteNode(m_Root);
+}
+
+void VmaBlockMetadata_Buddy::Init(VkDeviceSize size)
+{
+ VmaBlockMetadata::Init(size);
+
+ m_UsableSize = VmaPrevPow2(size);
+ m_SumFreeSize = m_UsableSize;
+
+ // Calculate m_LevelCount.
+ m_LevelCount = 1;
+ while(m_LevelCount < MAX_LEVELS &&
+ LevelToNodeSize(m_LevelCount) >= MIN_NODE_SIZE)
+ {
+ ++m_LevelCount;
+ }
+
+ Node* rootNode = vma_new(GetAllocationCallbacks(), Node)();
+ rootNode->offset = 0;
+ rootNode->type = Node::TYPE_FREE;
+ rootNode->parent = VMA_NULL;
+ rootNode->buddy = VMA_NULL;
+
+ m_Root = rootNode;
+ AddToFreeListFront(0, rootNode);
+}
+
+bool VmaBlockMetadata_Buddy::Validate() const
+{
+ // Validate tree.
+ ValidationContext ctx;
+ if(!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0)))
+ {
+ VMA_VALIDATE(false && "ValidateNode failed.");
+ }
+ VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);
+ VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize);
+
+ // Validate free node lists.
+ for(uint32_t level = 0; level < m_LevelCount; ++level)
+ {
+ VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||
+ m_FreeList[level].front->free.prev == VMA_NULL);
+
+ for(Node* node = m_FreeList[level].front;
+ node != VMA_NULL;
+ node = node->free.next)
+ {
+ VMA_VALIDATE(node->type == Node::TYPE_FREE);
+
+ if(node->free.next == VMA_NULL)
+ {
+ VMA_VALIDATE(m_FreeList[level].back == node);
+ }
+ else
+ {
+ VMA_VALIDATE(node->free.next->free.prev == node);
+ }
+ }
+ }
+
+ // Validate that free lists ar higher levels are empty.
+ for(uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level)
+ {
+ VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL);
+ }
+
+ return true;
+}
+
+VkDeviceSize VmaBlockMetadata_Buddy::GetUnusedRangeSizeMax() const
+{
+ for(uint32_t level = 0; level < m_LevelCount; ++level)
+ {
+ if(m_FreeList[level].front != VMA_NULL)
+ {
+ return LevelToNodeSize(level);
+ }
+ }
+ return 0;
+}
+
+void VmaBlockMetadata_Buddy::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
+{
+ const VkDeviceSize unusableSize = GetUnusableSize();
+
+ outInfo.blockCount = 1;
+
+ outInfo.allocationCount = outInfo.unusedRangeCount = 0;
+ outInfo.usedBytes = outInfo.unusedBytes = 0;
+
+ outInfo.allocationSizeMax = outInfo.unusedRangeSizeMax = 0;
+ outInfo.allocationSizeMin = outInfo.unusedRangeSizeMin = UINT64_MAX;
+ outInfo.allocationSizeAvg = outInfo.unusedRangeSizeAvg = 0; // Unused.
+
+ CalcAllocationStatInfoNode(outInfo, m_Root, LevelToNodeSize(0));
+
+ if(unusableSize > 0)
+ {
+ ++outInfo.unusedRangeCount;
+ outInfo.unusedBytes += unusableSize;
+ outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusableSize);
+ outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusableSize);
+ }
+}
+
+void VmaBlockMetadata_Buddy::AddPoolStats(VmaPoolStats& inoutStats) const
+{
+ const VkDeviceSize unusableSize = GetUnusableSize();
+
+ inoutStats.size += GetSize();
+ inoutStats.unusedSize += m_SumFreeSize + unusableSize;
+ inoutStats.allocationCount += m_AllocationCount;
+ inoutStats.unusedRangeCount += m_FreeCount;
+ inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
+
+ if(unusableSize > 0)
+ {
+ ++inoutStats.unusedRangeCount;
+ // Not updating inoutStats.unusedRangeSizeMax with unusableSize because this space is not available for allocations.
+ }
+}
+
+#if VMA_STATS_STRING_ENABLED
+
+void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const
+{
+ // TODO optimize
+ VmaStatInfo stat;
+ CalcAllocationStatInfo(stat);
+
+ PrintDetailedMap_Begin(
+ json,
+ stat.unusedBytes,
+ stat.allocationCount,
+ stat.unusedRangeCount);
+
+ PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0));
+
+ const VkDeviceSize unusableSize = GetUnusableSize();
+ if(unusableSize > 0)
+ {
+ PrintDetailedMap_UnusedRange(json,
+ m_UsableSize, // offset
+ unusableSize); // size
+ }
+
+ PrintDetailedMap_End(json);
+}
+
+#endif // #if VMA_STATS_STRING_ENABLED
+
+bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
+ uint32_t /*currentFrameIndex*/,
+ uint32_t /*frameInUseCount*/,
+ VkDeviceSize bufferImageGranularity,
+ VkDeviceSize allocSize,
+ VkDeviceSize allocAlignment,
+ bool upperAddress,
+ VmaSuballocationType allocType,
+ bool /*canMakeOtherLost*/,
+ uint32_t /*strategy*/,
+ VmaAllocationRequest* pAllocationRequest)
+{
+ VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
+ (void) upperAddress;
+
+ // Simple way to respect bufferImageGranularity. May be optimized some day.
+ // Whenever it might be an OPTIMAL image...
+ if(allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
+ allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
+ allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
+ {
+ allocAlignment = VMA_MAX(allocAlignment, bufferImageGranularity);
+ allocSize = VMA_MAX(allocSize, bufferImageGranularity);
+ }
+
+ if(allocSize > m_UsableSize)
+ {
+ return false;
+ }
+
+ const uint32_t targetLevel = AllocSizeToLevel(allocSize);
+ for(uint32_t level = targetLevel + 1; level--; )
+ {
+ for(Node* freeNode = m_FreeList[level].front;
+ freeNode != VMA_NULL;
+ freeNode = freeNode->free.next)
+ {
+ if(freeNode->offset % allocAlignment == 0)
+ {
+ pAllocationRequest->offset = freeNode->offset;
+ pAllocationRequest->sumFreeSize = LevelToNodeSize(level);
+ pAllocationRequest->sumItemSize = 0;
+ pAllocationRequest->itemsToMakeLostCount = 0;
+ pAllocationRequest->customData = (void*)(uintptr_t)level;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost(
+ uint32_t /*currentFrameIndex*/,
+ uint32_t /*frameInUseCount*/,
+ VmaAllocationRequest* pAllocationRequest)
+{
+ /*
+ Lost allocations are not supported in buddy allocator at the moment.
+ Support might be added in the future.
+ */
+ return pAllocationRequest->itemsToMakeLostCount == 0;
+}
+
+uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t /*currentFrameIndex*/, uint32_t /*frameInUseCount*/)
+{
+ /*
+ Lost allocations are not supported in buddy allocator at the moment.
+ Support might be added in the future.
+ */
+ return 0;
+}
+
+void VmaBlockMetadata_Buddy::Alloc(
+ const VmaAllocationRequest& request,
+ VmaSuballocationType /*type*/,
+ VkDeviceSize allocSize,
+ bool /*upperAddress*/,
+ VmaAllocation hAllocation)
+{
+ const uint32_t targetLevel = AllocSizeToLevel(allocSize);
+ uint32_t currLevel = (uint32_t)(uintptr_t)request.customData;
+
+ Node* currNode = m_FreeList[currLevel].front;
+ VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
+ while(currNode->offset != request.offset)
+ {
+ currNode = currNode->free.next;
+ VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
+ }
+
+ // Go down, splitting free nodes.
+ while(currLevel < targetLevel)
+ {
+ // currNode is already first free node at currLevel.
+ // Remove it from list of free nodes at this currLevel.
+ RemoveFromFreeList(currLevel, currNode);
+
+ const uint32_t childrenLevel = currLevel + 1;
+
+ // Create two free sub-nodes.
+ Node* leftChild = vma_new(GetAllocationCallbacks(), Node)();
+ Node* rightChild = vma_new(GetAllocationCallbacks(), Node)();
+
+ leftChild->offset = currNode->offset;
+ leftChild->type = Node::TYPE_FREE;
+ leftChild->parent = currNode;
+ leftChild->buddy = rightChild;
+
+ rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel);
+ rightChild->type = Node::TYPE_FREE;
+ rightChild->parent = currNode;
+ rightChild->buddy = leftChild;
+
+ // Convert current currNode to split type.
+ currNode->type = Node::TYPE_SPLIT;
+ currNode->split.leftChild = leftChild;
+
+ // Add child nodes to free list. Order is important!
+ AddToFreeListFront(childrenLevel, rightChild);
+ AddToFreeListFront(childrenLevel, leftChild);
+
+ ++m_FreeCount;
+ //m_SumFreeSize -= LevelToNodeSize(currLevel) % 2; // Useful only when level node sizes can be non power of 2.
+ ++currLevel;
+ currNode = m_FreeList[currLevel].front;
+
+ /*
+ We can be sure that currNode, as left child of node previously split,
+ also fullfills the alignment requirement.
+ */
+ }
+
+ // Remove from free list.
+ VMA_ASSERT(currLevel == targetLevel &&
+ currNode != VMA_NULL &&
+ currNode->type == Node::TYPE_FREE);
+ RemoveFromFreeList(currLevel, currNode);
+
+ // Convert to allocation node.
+ currNode->type = Node::TYPE_ALLOCATION;
+ currNode->allocation.alloc = hAllocation;
+
+ ++m_AllocationCount;
+ --m_FreeCount;
+ m_SumFreeSize -= allocSize;
+}
+
+void VmaBlockMetadata_Buddy::DeleteNode(Node* node)
+{
+ if(node->type == Node::TYPE_SPLIT)
+ {
+ DeleteNode(node->split.leftChild->buddy);
+ DeleteNode(node->split.leftChild);
+ }
+
+ vma_delete(GetAllocationCallbacks(), node);
+}
+
+bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const
+{
+ VMA_VALIDATE(level < m_LevelCount);
+ VMA_VALIDATE(curr->parent == parent);
+ VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL));
+ VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr);
+ switch(curr->type)
+ {
+ case Node::TYPE_FREE:
+ // curr->free.prev, next are validated separately.
+ ctx.calculatedSumFreeSize += levelNodeSize;
+ ++ctx.calculatedFreeCount;
+ break;
+ case Node::TYPE_ALLOCATION:
+ ++ctx.calculatedAllocationCount;
+ ctx.calculatedSumFreeSize += levelNodeSize - curr->allocation.alloc->GetSize();
+ VMA_VALIDATE(curr->allocation.alloc != VK_NULL_HANDLE);
+ break;
+ case Node::TYPE_SPLIT:
+ {
+ const uint32_t childrenLevel = level + 1;
+ const VkDeviceSize childrenLevelNodeSize = levelNodeSize / 2;
+ const Node* const leftChild = curr->split.leftChild;
+ VMA_VALIDATE(leftChild != VMA_NULL);
+ VMA_VALIDATE(leftChild->offset == curr->offset);
+ if(!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize))
+ {
+ VMA_VALIDATE(false && "ValidateNode for left child failed.");
+ }
+ const Node* const rightChild = leftChild->buddy;
+ VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize);
+ if(!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize))
+ {
+ VMA_VALIDATE(false && "ValidateNode for right child failed.");
+ }
+ }
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const
+{
+ // I know this could be optimized somehow e.g. by using std::log2p1 from C++20.
+ uint32_t level = 0;
+ VkDeviceSize currLevelNodeSize = m_UsableSize;
+ VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1;
+ while(allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount)
+ {
+ ++level;
+ currLevelNodeSize = nextLevelNodeSize;
+ nextLevelNodeSize = currLevelNodeSize >> 1;
+ }
+ return level;
+}
+
+void VmaBlockMetadata_Buddy::FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset)
+{
+ // Find node and level.
+ Node* node = m_Root;
+ VkDeviceSize nodeOffset = 0;
+ uint32_t level = 0;
+ VkDeviceSize levelNodeSize = LevelToNodeSize(0);
+ while(node->type == Node::TYPE_SPLIT)
+ {
+ const VkDeviceSize nextLevelSize = levelNodeSize >> 1;
+ if(offset < nodeOffset + nextLevelSize)
+ {
+ node = node->split.leftChild;
+ }
+ else
+ {
+ node = node->split.leftChild->buddy;
+ nodeOffset += nextLevelSize;
+ }
+ ++level;
+ levelNodeSize = nextLevelSize;
+ }
+
+ VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);
+ VMA_ASSERT(alloc == VK_NULL_HANDLE || node->allocation.alloc == alloc);
+
+ ++m_FreeCount;
+ --m_AllocationCount;
+ m_SumFreeSize += alloc->GetSize();
+
+ node->type = Node::TYPE_FREE;
+
+ // Join free nodes if possible.
+ while(level > 0 && node->buddy->type == Node::TYPE_FREE)
+ {
+ RemoveFromFreeList(level, node->buddy);
+ Node* const parent = node->parent;
+
+ vma_delete(GetAllocationCallbacks(), node->buddy);
+ vma_delete(GetAllocationCallbacks(), node);
+ parent->type = Node::TYPE_FREE;
+
+ node = parent;
+ --level;
+ //m_SumFreeSize += LevelToNodeSize(level) % 2; // Useful only when level node sizes can be non power of 2.
+ --m_FreeCount;
+ }
+
+ AddToFreeListFront(level, node);
+}
+
+void VmaBlockMetadata_Buddy::CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const
+{
+ switch(node->type)
+ {
+ case Node::TYPE_FREE:
+ ++outInfo.unusedRangeCount;
+ outInfo.unusedBytes += levelNodeSize;
+ outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, levelNodeSize);
+ outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, levelNodeSize);
+ break;
+ case Node::TYPE_ALLOCATION:
+ {
+ const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
+ ++outInfo.allocationCount;
+ outInfo.usedBytes += allocSize;
+ outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, allocSize);
+ outInfo.allocationSizeMin = VMA_MAX(outInfo.allocationSizeMin, allocSize);
+
+ const VkDeviceSize unusedRangeSize = levelNodeSize - allocSize;
+ if(unusedRangeSize > 0)
+ {
+ ++outInfo.unusedRangeCount;
+ outInfo.unusedBytes += unusedRangeSize;
+ outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusedRangeSize);
+ outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, unusedRangeSize);
+ }
+ }
+ break;
+ case Node::TYPE_SPLIT:
+ {
+ const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
+ const Node* const leftChild = node->split.leftChild;
+ CalcAllocationStatInfoNode(outInfo, leftChild, childrenNodeSize);
+ const Node* const rightChild = leftChild->buddy;
+ CalcAllocationStatInfoNode(outInfo, rightChild, childrenNodeSize);
+ }
+ break;
+ default:
+ VMA_ASSERT(0);
+ }
+}
+
+void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node)
+{
+ VMA_ASSERT(node->type == Node::TYPE_FREE);
+
+ // List is empty.
+ Node* const frontNode = m_FreeList[level].front;
+ if(frontNode == VMA_NULL)
+ {
+ VMA_ASSERT(m_FreeList[level].back == VMA_NULL);
+ node->free.prev = node->free.next = VMA_NULL;
+ m_FreeList[level].front = m_FreeList[level].back = node;
+ }
+ else
+ {
+ VMA_ASSERT(frontNode->free.prev == VMA_NULL);
+ node->free.prev = VMA_NULL;
+ node->free.next = frontNode;
+ frontNode->free.prev = node;
+ m_FreeList[level].front = node;
+ }
+}
+
+void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node)
+{
+ VMA_ASSERT(m_FreeList[level].front != VMA_NULL);
+
+ // It is at the front.
+ if(node->free.prev == VMA_NULL)
+ {
+ VMA_ASSERT(m_FreeList[level].front == node);
+ m_FreeList[level].front = node->free.next;
+ }
+ else
+ {
+ Node* const prevFreeNode = node->free.prev;
+ VMA_ASSERT(prevFreeNode->free.next == node);
+ prevFreeNode->free.next = node->free.next;
+ }
+
+ // It is at the back.
+ if(node->free.next == VMA_NULL)
+ {
+ VMA_ASSERT(m_FreeList[level].back == node);
+ m_FreeList[level].back = node->free.prev;
+ }
+ else
+ {
+ Node* const nextFreeNode = node->free.next;
+ VMA_ASSERT(nextFreeNode->free.prev == node);
+ nextFreeNode->free.prev = node->free.prev;
+ }
+}
+
+#if VMA_STATS_STRING_ENABLED
+void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const
+{
+ switch(node->type)
+ {
+ case Node::TYPE_FREE:
+ PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize);
+ break;
+ case Node::TYPE_ALLOCATION:
+ {
+ PrintDetailedMap_Allocation(json, node->offset, node->allocation.alloc);
+ const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
+ if(allocSize < levelNodeSize)
+ {
+ PrintDetailedMap_UnusedRange(json, node->offset + allocSize, levelNodeSize - allocSize);
+ }
+ }
+ break;
+ case Node::TYPE_SPLIT:
+ {
+ const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
+ const Node* const leftChild = node->split.leftChild;
+ PrintDetailedMapNode(json, leftChild, childrenNodeSize);
+ const Node* const rightChild = leftChild->buddy;
+ PrintDetailedMapNode(json, rightChild, childrenNodeSize);
+ }
+ break;
+ default:
+ VMA_ASSERT(0);
+ }
+}
+#endif // #if VMA_STATS_STRING_ENABLED
+
+
+////////////////////////////////////////////////////////////////////////////////
+// class VmaDeviceMemoryBlock
+
+VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator /*hAllocator*/) :
+ m_pMetadata(VMA_NULL),
+ m_MemoryTypeIndex(UINT32_MAX),
+ m_Id(0),
+ m_hMemory(VK_NULL_HANDLE),
+ m_MapCount(0),
+ m_pMappedData(VMA_NULL)
+{
+}
+
+void VmaDeviceMemoryBlock::Init(
+ VmaAllocator hAllocator,
+ uint32_t newMemoryTypeIndex,
+ VkDeviceMemory newMemory,
+ VkDeviceSize newSize,
+ uint32_t id,
+ uint32_t algorithm)
+{
+ VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
+
+ m_MemoryTypeIndex = newMemoryTypeIndex;
+ m_Id = id;
+ m_hMemory = newMemory;
+
+ switch(algorithm)
+ {
+ case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
+ m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator);
+ break;
+ case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
+ m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Buddy)(hAllocator);
+ break;
+ default:
+ VMA_ASSERT(0);
+ // Fall-through.
+ case 0:
+ m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Generic)(hAllocator);
+ }
+ m_pMetadata->Init(newSize);
+}
+
+void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
+{
+ // This is the most important assert in the entire library.
+ // Hitting it means you have some memory leak - unreleased VmaAllocation objects.
+ VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
+
+ VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
+ allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);
+ m_hMemory = VK_NULL_HANDLE;
+
+ vma_delete(allocator, m_pMetadata);
+ m_pMetadata = VMA_NULL;
+}
+
+bool VmaDeviceMemoryBlock::Validate() const
+{
+ VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&
+ (m_pMetadata->GetSize() != 0));
+
+ return m_pMetadata->Validate();
+}
+
+VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)
+{
+ void* pData = nullptr;
+ VkResult res = Map(hAllocator, 1, &pData);
+ if(res != VK_SUCCESS)
+ {
+ return res;
+ }
+
+ res = m_pMetadata->CheckCorruption(pData);
+
+ Unmap(hAllocator, 1);
+
+ return res;
+}
+
+VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
+{
+ if(count == 0)
+ {
+ return VK_SUCCESS;
+ }
+
+ VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
+ if(m_MapCount != 0)
+ {
+ m_MapCount += count;
+ VMA_ASSERT(m_pMappedData != VMA_NULL);
+ if(ppData != VMA_NULL)
+ {
+ *ppData = m_pMappedData;
+ }
+ return VK_SUCCESS;
+ }
+ else
+ {
+ VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
+ hAllocator->m_hDevice,
+ m_hMemory,
+ 0, // offset
+ VK_WHOLE_SIZE,
+ 0, // flags
+ &m_pMappedData);
+ if(result == VK_SUCCESS)
+ {
+ if(ppData != VMA_NULL)
+ {
+ *ppData = m_pMappedData;
+ }
+ m_MapCount = count;
+ }
+ return result;
+ }
+}
+
+void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
+{
+ if(count == 0)
+ {
+ return;
+ }
+
+ VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
+ if(m_MapCount >= count)
+ {
+ m_MapCount -= count;
+ if(m_MapCount == 0)
+ {
+ m_pMappedData = VMA_NULL;
+ (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
+ }
+ }
+ else
+ {
+ VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
+ }
+}
+
+VkResult VmaDeviceMemoryBlock::WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
+{
+ VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
+ VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
+
+ void* pData;
+ VkResult res = Map(hAllocator, 1, &pData);
+ if(res != VK_SUCCESS)
+ {
+ return res;
+ }
+
+ VmaWriteMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN);
+ VmaWriteMagicValue(pData, allocOffset + allocSize);
+
+ Unmap(hAllocator, 1);
+
+ return VK_SUCCESS;
+}
+
+VkResult VmaDeviceMemoryBlock::ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
+{
+ VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
+ VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
+
+ void* pData;
+ VkResult res = Map(hAllocator, 1, &pData);
+ if(res != VK_SUCCESS)
+ {
+ return res;
+ }
+
+ if(!VmaValidateMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN))
+ {
+ VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE FREED ALLOCATION!");
+ }
+ else if(!VmaValidateMagicValue(pData, allocOffset + allocSize))
+ {
+ VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");
+ }
+
+ Unmap(hAllocator, 1);
+
+ return VK_SUCCESS;
+}
+
+VkResult VmaDeviceMemoryBlock::BindBufferMemory(
+ const VmaAllocator hAllocator,
+ const VmaAllocation hAllocation,
+ VkBuffer hBuffer)
+{
+ VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
+ hAllocation->GetBlock() == this);
+ // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
+ VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
+ return hAllocator->GetVulkanFunctions().vkBindBufferMemory(
+ hAllocator->m_hDevice,
+ hBuffer,
+ m_hMemory,
+ hAllocation->GetOffset());
+}
+
+VkResult VmaDeviceMemoryBlock::BindImageMemory(
+ const VmaAllocator hAllocator,
+ const VmaAllocation hAllocation,
+ VkImage hImage)
+{
+ VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
+ hAllocation->GetBlock() == this);
+ // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
+ VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
+ return hAllocator->GetVulkanFunctions().vkBindImageMemory(
+ hAllocator->m_hDevice,
+ hImage,
+ m_hMemory,
+ hAllocation->GetOffset());
+}
+
+static void InitStatInfo(VmaStatInfo& outInfo)
+{
+ memset(&outInfo, 0, sizeof(outInfo));
+ outInfo.allocationSizeMin = UINT64_MAX;
+ outInfo.unusedRangeSizeMin = UINT64_MAX;
+}
+
+// Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo.
+static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo)
+{
+ inoutInfo.blockCount += srcInfo.blockCount;
+ inoutInfo.allocationCount += srcInfo.allocationCount;
+ inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount;
+ inoutInfo.usedBytes += srcInfo.usedBytes;
+ inoutInfo.unusedBytes += srcInfo.unusedBytes;
+ inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin);
+ inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax);
+ inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin);
+ inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax);
+}
+
+static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo)
+{
+ inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ?
+ VmaRoundDiv<VkDeviceSize>(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0;
+ inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ?
+ VmaRoundDiv<VkDeviceSize>(inoutInfo.unusedBytes, inoutInfo.unusedRangeCount) : 0;
+}
+
+VmaPool_T::VmaPool_T(
+ VmaAllocator hAllocator,
+ const VmaPoolCreateInfo& createInfo,
+ VkDeviceSize preferredBlockSize) :
+ m_BlockVector(
+ hAllocator,
+ createInfo.memoryTypeIndex,
+ createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
+ createInfo.minBlockCount,
+ createInfo.maxBlockCount,
+ (createInfo.flags & VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
+ createInfo.frameInUseCount,
+ true, // isCustomPool
+ createInfo.blockSize != 0, // explicitBlockSize
+ createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK), // algorithm
+ m_Id(0)
+{
+}
+
+VmaPool_T::~VmaPool_T()
+{
+}
+
+#if VMA_STATS_STRING_ENABLED
+
+#endif // #if VMA_STATS_STRING_ENABLED
+
+VmaBlockVector::VmaBlockVector(
+ VmaAllocator hAllocator,
+ uint32_t memoryTypeIndex,
+ VkDeviceSize preferredBlockSize,
+ size_t minBlockCount,
+ size_t maxBlockCount,
+ VkDeviceSize bufferImageGranularity,
+ uint32_t frameInUseCount,
+ bool isCustomPool,
+ bool explicitBlockSize,
+ uint32_t algorithm) :
+ m_hAllocator(hAllocator),
+ m_MemoryTypeIndex(memoryTypeIndex),
+ m_PreferredBlockSize(preferredBlockSize),
+ m_MinBlockCount(minBlockCount),
+ m_MaxBlockCount(maxBlockCount),
+ m_BufferImageGranularity(bufferImageGranularity),
+ m_FrameInUseCount(frameInUseCount),
+ m_IsCustomPool(isCustomPool),
+ m_ExplicitBlockSize(explicitBlockSize),
+ m_Algorithm(algorithm),
+ m_HasEmptyBlock(false),
+ m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
+ m_NextBlockId(0)
+{
+}
+
+VmaBlockVector::~VmaBlockVector()
+{
+ for(size_t i = m_Blocks.size(); i--; )
+ {
+ m_Blocks[i]->Destroy(m_hAllocator);
+ vma_delete(m_hAllocator, m_Blocks[i]);
+ }
+}
+
+VkResult VmaBlockVector::CreateMinBlocks()
+{
+ for(size_t i = 0; i < m_MinBlockCount; ++i)
+ {
+ VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
+ if(res != VK_SUCCESS)
+ {
+ return res;
+ }
+ }
+ return VK_SUCCESS;
+}
+
+void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats)
+{
+ VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
+
+ const size_t blockCount = m_Blocks.size();
+
+ pStats->size = 0;
+ pStats->unusedSize = 0;
+ pStats->allocationCount = 0;
+ pStats->unusedRangeCount = 0;
+ pStats->unusedRangeSizeMax = 0;
+ pStats->blockCount = blockCount;
+
+ for(uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
+ {
+ const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
+ VMA_ASSERT(pBlock);
+ VMA_HEAVY_ASSERT(pBlock->Validate());
+ pBlock->m_pMetadata->AddPoolStats(*pStats);
+ }
+}
+
+bool VmaBlockVector::IsCorruptionDetectionEnabled() const
+{
+ const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
+ return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&
+ (VMA_DEBUG_MARGIN > 0) &&
+ (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;
+}
+
+static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
+
+VkResult VmaBlockVector::Allocate(
+ VmaPool hCurrentPool,
+ uint32_t currentFrameIndex,
+ VkDeviceSize size,
+ VkDeviceSize alignment,
+ const VmaAllocationCreateInfo& createInfo,
+ VmaSuballocationType suballocType,
+ size_t allocationCount,
+ VmaAllocation* pAllocations)
+{
+ size_t allocIndex;
+ VkResult res = VK_SUCCESS;
+
+ {
+ VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
+ for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
+ {
+ res = AllocatePage(
+ hCurrentPool,
+ currentFrameIndex,
+ size,
+ alignment,
+ createInfo,
+ suballocType,
+ pAllocations + allocIndex);
+ if(res != VK_SUCCESS)
+ {
+ break;
+ }
+ }
+ }
+
+ if(res != VK_SUCCESS)
+ {
+ // Free all already created allocations.
+ while(allocIndex--)
+ {
+ Free(pAllocations[allocIndex]);
+ }
+ memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
+ }
+
+ return res;
+}
+
+VkResult VmaBlockVector::AllocatePage(
+ VmaPool hCurrentPool,
+ uint32_t currentFrameIndex,
+ VkDeviceSize size,
+ VkDeviceSize alignment,
+ const VmaAllocationCreateInfo& createInfo,
+ VmaSuballocationType suballocType,
+ VmaAllocation* pAllocation)
+{
+ const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
+ bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
+ const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
+ const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
+ const bool canCreateNewBlock =
+ ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
+ (m_Blocks.size() < m_MaxBlockCount);
+ uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
+
+ // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
+ // Which in turn is available only when maxBlockCount = 1.
+ if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT && m_MaxBlockCount > 1)
+ {
+ canMakeOtherLost = false;
+ }
+
+ // Upper address can only be used with linear allocator and within single memory block.
+ if(isUpperAddress &&
+ (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
+ {
+ return VK_ERROR_FEATURE_NOT_PRESENT;
+ }
+
+ // Validate strategy.
+ switch(strategy)
+ {
+ case 0:
+ strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
+ break;
+ case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT:
+ case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT:
+ case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT:
+ break;
+ default:
+ return VK_ERROR_FEATURE_NOT_PRESENT;
+ }
+
+ // Early reject: requested allocation size is larger that maximum block size for this block vector.
+ if(size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize)
+ {
+ return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+ }
+
+ /*
+ Under certain condition, this whole section can be skipped for optimization, so
+ we move on directly to trying to allocate with canMakeOtherLost. That's the case
+ e.g. for custom pools with linear algorithm.
+ */
+ if(!canMakeOtherLost || canCreateNewBlock)
+ {
+ // 1. Search existing allocations. Try to allocate without making other allocations lost.
+ VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags;
+ allocFlagsCopy &= ~VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
+
+ if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
+ {
+ // Use only last block.
+ if(!m_Blocks.empty())
+ {
+ VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
+ VMA_ASSERT(pCurrBlock);
+ VkResult res = AllocateFromBlock(
+ pCurrBlock,
+ hCurrentPool,
+ currentFrameIndex,
+ size,
+ alignment,
+ allocFlagsCopy,
+ createInfo.pUserData,
+ suballocType,
+ strategy,
+ pAllocation);
+ if(res == VK_SUCCESS)
+ {
+ VMA_DEBUG_LOG(" Returned from last block #%u", (uint32_t)(m_Blocks.size() - 1));
+ return VK_SUCCESS;
+ }
+ }
+ }
+ else
+ {
+ if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
+ {
+ // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
+ for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
+ {
+ VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
+ VMA_ASSERT(pCurrBlock);
+ VkResult res = AllocateFromBlock(
+ pCurrBlock,
+ hCurrentPool,
+ currentFrameIndex,
+ size,
+ alignment,
+ allocFlagsCopy,
+ createInfo.pUserData,
+ suballocType,
+ strategy,
+ pAllocation);
+ if(res == VK_SUCCESS)
+ {
+ VMA_DEBUG_LOG(" Returned from existing block #%u", (uint32_t)blockIndex);
+ return VK_SUCCESS;
+ }
+ }
+ }
+ else // WORST_FIT, FIRST_FIT
+ {
+ // Backward order in m_Blocks - prefer blocks with largest amount of free space.
+ for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
+ {
+ VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
+ VMA_ASSERT(pCurrBlock);
+ VkResult res = AllocateFromBlock(
+ pCurrBlock,
+ hCurrentPool,
+ currentFrameIndex,
+ size,
+ alignment,
+ allocFlagsCopy,
+ createInfo.pUserData,
+ suballocType,
+ strategy,
+ pAllocation);
+ if(res == VK_SUCCESS)
+ {
+ VMA_DEBUG_LOG(" Returned from existing block #%u", (uint32_t)blockIndex);
+ return VK_SUCCESS;
+ }
+ }
+ }
+ }
+
+ // 2. Try to create new block.
+ if(canCreateNewBlock)
+ {
+ // Calculate optimal size for new block.
+ VkDeviceSize newBlockSize = m_PreferredBlockSize;
+ uint32_t newBlockSizeShift = 0;
+ const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
+
+ if(!m_ExplicitBlockSize)
+ {
+ // Allocate 1/8, 1/4, 1/2 as first blocks.
+ const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
+ for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
+ {
+ const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
+ if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
+ {
+ newBlockSize = smallerNewBlockSize;
+ ++newBlockSizeShift;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ size_t newBlockIndex = 0;
+ VkResult res = CreateBlock(newBlockSize, &newBlockIndex);
+ // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
+ if(!m_ExplicitBlockSize)
+ {
+ while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
+ {
+ const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
+ if(smallerNewBlockSize >= size)
+ {
+ newBlockSize = smallerNewBlockSize;
+ ++newBlockSizeShift;
+ res = CreateBlock(newBlockSize, &newBlockIndex);
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ if(res == VK_SUCCESS)
+ {
+ VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
+ VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
+
+ res = AllocateFromBlock(
+ pBlock,
+ hCurrentPool,
+ currentFrameIndex,
+ size,
+ alignment,
+ allocFlagsCopy,
+ createInfo.pUserData,
+ suballocType,
+ strategy,
+ pAllocation);
+ if(res == VK_SUCCESS)
+ {
+ VMA_DEBUG_LOG(" Created new block Size=%llu", newBlockSize);
+ return VK_SUCCESS;
+ }
+ else
+ {
+ // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
+ return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+ }
+ }
+ }
+ }
+
+ // 3. Try to allocate from existing blocks with making other allocations lost.
+ if(canMakeOtherLost)
+ {
+ uint32_t tryIndex = 0;
+ for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex)
+ {
+ VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL;
+ VmaAllocationRequest bestRequest = {};
+ VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
+
+ // 1. Search existing allocations.
+ if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
+ {
+ // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
+ for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
+ {
+ VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
+ VMA_ASSERT(pCurrBlock);
+ VmaAllocationRequest currRequest = {};
+ if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
+ currentFrameIndex,
+ m_FrameInUseCount,
+ m_BufferImageGranularity,
+ size,
+ alignment,
+ (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
+ suballocType,
+ canMakeOtherLost,
+ strategy,
+ &currRequest))
+ {
+ const VkDeviceSize currRequestCost = currRequest.CalcCost();
+ if(pBestRequestBlock == VMA_NULL ||
+ currRequestCost < bestRequestCost)
+ {
+ pBestRequestBlock = pCurrBlock;
+ bestRequest = currRequest;
+ bestRequestCost = currRequestCost;
+
+ if(bestRequestCost == 0)
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
+ else // WORST_FIT, FIRST_FIT
+ {
+ // Backward order in m_Blocks - prefer blocks with largest amount of free space.
+ for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
+ {
+ VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
+ VMA_ASSERT(pCurrBlock);
+ VmaAllocationRequest currRequest = {};
+ if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
+ currentFrameIndex,
+ m_FrameInUseCount,
+ m_BufferImageGranularity,
+ size,
+ alignment,
+ (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
+ suballocType,
+ canMakeOtherLost,
+ strategy,
+ &currRequest))
+ {
+ const VkDeviceSize currRequestCost = currRequest.CalcCost();
+ if(pBestRequestBlock == VMA_NULL ||
+ currRequestCost < bestRequestCost ||
+ strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
+ {
+ pBestRequestBlock = pCurrBlock;
+ bestRequest = currRequest;
+ bestRequestCost = currRequestCost;
+
+ if(bestRequestCost == 0 ||
+ strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if(pBestRequestBlock != VMA_NULL)
+ {
+ if(mapped)
+ {
+ VkResult res = pBestRequestBlock->Map(m_hAllocator, 1, VMA_NULL);
+ if(res != VK_SUCCESS)
+ {
+ return res;
+ }
+ }
+
+ if(pBestRequestBlock->m_pMetadata->MakeRequestedAllocationsLost(
+ currentFrameIndex,
+ m_FrameInUseCount,
+ &bestRequest))
+ {
+ // We no longer have an empty Allocation.
+ if(pBestRequestBlock->m_pMetadata->IsEmpty())
+ {
+ m_HasEmptyBlock = false;
+ }
+ // Allocate from this pBlock.
+ *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
+ pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, isUpperAddress, *pAllocation);
+ (*pAllocation)->InitBlockAllocation(
+ hCurrentPool,
+ pBestRequestBlock,
+ bestRequest.offset,
+ alignment,
+ size,
+ suballocType,
+ mapped,
+ (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
+ VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
+ VMA_DEBUG_LOG(" Returned from existing allocation #%u", (uint32_t)blockIndex);
+ (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
+ if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
+ {
+ m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
+ }
+ if(IsCorruptionDetectionEnabled())
+ {
+ VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(m_hAllocator, bestRequest.offset, size);
+ (void) res;
+ VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
+ }
+ return VK_SUCCESS;
+ }
+ // else: Some allocations must have been touched while we are here. Next try.
+ }
+ else
+ {
+ // Could not find place in any of the blocks - break outer loop.
+ break;
+ }
+ }
+ /* Maximum number of tries exceeded - a very unlike event when many other
+ threads are simultaneously touching allocations making it impossible to make
+ lost at the same time as we try to allocate. */
+ if(tryIndex == VMA_ALLOCATION_TRY_COUNT)
+ {
+ return VK_ERROR_TOO_MANY_OBJECTS;
+ }
+ }
+
+ return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+}
+
+void VmaBlockVector::Free(
+ VmaAllocation hAllocation)
+{
+ VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
+
+ // Scope for lock.
+ {
+ VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
+
+ VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
+
+ if(IsCorruptionDetectionEnabled())
+ {
+ VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
+ (void) res;
+ VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
+ }
+
+ if(hAllocation->IsPersistentMap())
+ {
+ pBlock->Unmap(m_hAllocator, 1);
+ }
+
+ pBlock->m_pMetadata->Free(hAllocation);
+ VMA_HEAVY_ASSERT(pBlock->Validate());
+
+ VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", memTypeIndex);
+
+ // pBlock became empty after this deallocation.
+ if(pBlock->m_pMetadata->IsEmpty())
+ {
+ // Already has empty Allocation. We don't want to have two, so delete this one.
+ if(m_HasEmptyBlock && m_Blocks.size() > m_MinBlockCount)
+ {
+ pBlockToDelete = pBlock;
+ Remove(pBlock);
+ }
+ // We now have first empty block.
+ else
+ {
+ m_HasEmptyBlock = true;
+ }
+ }
+ // pBlock didn't become empty, but we have another empty block - find and free that one.
+ // (This is optional, heuristics.)
+ else if(m_HasEmptyBlock)
+ {
+ VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
+ if(pLastBlock->m_pMetadata->IsEmpty() && m_Blocks.size() > m_MinBlockCount)
+ {
+ pBlockToDelete = pLastBlock;
+ m_Blocks.pop_back();
+ m_HasEmptyBlock = false;
+ }
+ }
+
+ IncrementallySortBlocks();
+ }
+
+ // Destruction of a free Allocation. Deferred until this point, outside of mutex
+ // lock, for performance reason.
+ if(pBlockToDelete != VMA_NULL)
+ {
+ VMA_DEBUG_LOG(" Deleted empty allocation");
+ pBlockToDelete->Destroy(m_hAllocator);
+ vma_delete(m_hAllocator, pBlockToDelete);
+ }
+}
+
+VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
+{
+ VkDeviceSize result = 0;
+ for(size_t i = m_Blocks.size(); i--; )
+ {
+ result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
+ if(result >= m_PreferredBlockSize)
+ {
+ break;
+ }
+ }
+ return result;
+}
+
+void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
+{
+ for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
+ {
+ if(m_Blocks[blockIndex] == pBlock)
+ {
+ VmaVectorRemove(m_Blocks, blockIndex);
+ return;
+ }
+ }
+ VMA_ASSERT(0);
+}
+
+void VmaBlockVector::IncrementallySortBlocks()
+{
+ if(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
+ {
+ // Bubble sort only until first swap.
+ for(size_t i = 1; i < m_Blocks.size(); ++i)
+ {
+ if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
+ {
+ VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
+ return;
+ }
+ }
+ }
+}
+
+VkResult VmaBlockVector::AllocateFromBlock(
+ VmaDeviceMemoryBlock* pBlock,
+ VmaPool hCurrentPool,
+ uint32_t currentFrameIndex,
+ VkDeviceSize size,
+ VkDeviceSize alignment,
+ VmaAllocationCreateFlags allocFlags,
+ void* pUserData,
+ VmaSuballocationType suballocType,
+ uint32_t strategy,
+ VmaAllocation* pAllocation)
+{
+ VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0);
+ const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
+ const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
+ const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
+
+ VmaAllocationRequest currRequest = {};
+ if(pBlock->m_pMetadata->CreateAllocationRequest(
+ currentFrameIndex,
+ m_FrameInUseCount,
+ m_BufferImageGranularity,
+ size,
+ alignment,
+ isUpperAddress,
+ suballocType,
+ false, // canMakeOtherLost
+ strategy,
+ &currRequest))
+ {
+ // Allocate from pCurrBlock.
+ VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
+
+ if(mapped)
+ {
+ VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
+ if(res != VK_SUCCESS)
+ {
+ return res;
+ }
+ }
+
+ // We no longer have an empty Allocation.
+ if(pBlock->m_pMetadata->IsEmpty())
+ {
+ m_HasEmptyBlock = false;
+ }
+
+ *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
+ pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, isUpperAddress, *pAllocation);
+ (*pAllocation)->InitBlockAllocation(
+ hCurrentPool,
+ pBlock,
+ currRequest.offset,
+ alignment,
+ size,
+ suballocType,
+ mapped,
+ (allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
+ VMA_HEAVY_ASSERT(pBlock->Validate());
+ (*pAllocation)->SetUserData(m_hAllocator, pUserData);
+ if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
+ {
+ m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
+ }
+ if(IsCorruptionDetectionEnabled())
+ {
+ VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size);
+ (void) res;
+ VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
+ }
+ return VK_SUCCESS;
+ }
+ return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+}
+
+VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
+{
+ VkMemoryAllocateInfo allocInfo = {};
+ allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
+ allocInfo.allocationSize = blockSize;
+ VkDeviceMemory mem = VK_NULL_HANDLE;
+ VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
+ if(res < 0)
+ {
+ return res;
+ }
+
+ // New VkDeviceMemory successfully created.
+
+ // Create new Allocation for it.
+ VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
+ pBlock->Init(
+ m_hAllocator,
+ m_MemoryTypeIndex,
+ mem,
+ allocInfo.allocationSize,
+ m_NextBlockId++,
+ m_Algorithm);
+
+ m_Blocks.push_back(pBlock);
+ if(pNewBlockIndex != VMA_NULL)
+ {
+ *pNewBlockIndex = m_Blocks.size() - 1;
+ }
+
+ return VK_SUCCESS;
+}
+
+void VmaBlockVector::ApplyDefragmentationMovesCpu(
+ class VmaBlockVectorDefragmentationContext* pDefragCtx,
+ const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves)
+{
+ const size_t blockCount = m_Blocks.size();
+ const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_MemoryTypeIndex);
+
+ enum BLOCK_FLAG
+ {
+ BLOCK_FLAG_USED = 0x00000001,
+ BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002,
+ };
+
+ struct BlockInfo
+ {
+ uint32_t flags;
+ void* pMappedData;
+ };
+ VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> >
+ blockInfo(blockCount, VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks()));
+ memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo));
+
+ // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
+ const size_t moveCount = moves.size();
+ for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
+ {
+ const VmaDefragmentationMove& move = moves[moveIndex];
+ blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED;
+ blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED;
+ }
+
+ VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
+
+ // Go over all blocks. Get mapped pointer or map if necessary.
+ for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
+ {
+ BlockInfo& currBlockInfo = blockInfo[blockIndex];
+ VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
+ if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0)
+ {
+ currBlockInfo.pMappedData = pBlock->GetMappedData();
+ // It is not originally mapped - map it.
+ if(currBlockInfo.pMappedData == VMA_NULL)
+ {
+ pDefragCtx->res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData);
+ if(pDefragCtx->res == VK_SUCCESS)
+ {
+ currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION;
+ }
+ }
+ }
+ }
+
+ // Go over all moves. Do actual data transfer.
+ if(pDefragCtx->res == VK_SUCCESS)
+ {
+ const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
+ VkMappedMemoryRange memRange = {};
+ memRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
+
+ for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
+ {
+ const VmaDefragmentationMove& move = moves[moveIndex];
+
+ const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex];
+ const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex];
+
+ VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData);
+
+ // Invalidate source.
+ if(isNonCoherent)
+ {
+ VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex];
+ memRange.memory = pSrcBlock->GetDeviceMemory();
+ memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize);
+ memRange.size = VMA_MIN(
+ VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize),
+ pSrcBlock->m_pMetadata->GetSize() - memRange.offset);
+ (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
+ }
+
+ // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
+ memmove(
+ reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
+ reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
+ static_cast<size_t>(move.size));
+
+ if(IsCorruptionDetectionEnabled())
+ {
+ VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN);
+ VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size);
+ }
+
+ // Flush destination.
+ if(isNonCoherent)
+ {
+ VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex];
+ memRange.memory = pDstBlock->GetDeviceMemory();
+ memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize);
+ memRange.size = VMA_MIN(
+ VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize),
+ pDstBlock->m_pMetadata->GetSize() - memRange.offset);
+ (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
+ }
+ }
+ }
+
+ // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation.
+ // Regardless of pCtx->res == VK_SUCCESS.
+ for(size_t blockIndex = blockCount; blockIndex--; )
+ {
+ const BlockInfo& currBlockInfo = blockInfo[blockIndex];
+ if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0)
+ {
+ VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
+ pBlock->Unmap(m_hAllocator, 1);
+ }
+ }
+}
+
+void VmaBlockVector::ApplyDefragmentationMovesGpu(
+ class VmaBlockVectorDefragmentationContext* pDefragCtx,
+ const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
+ VkCommandBuffer commandBuffer)
+{
+ const size_t blockCount = m_Blocks.size();
+
+ pDefragCtx->blockContexts.resize(blockCount);
+ for (size_t i = 0; i < blockCount; ++i)
+ pDefragCtx->blockContexts[i] = VmaBlockDefragmentationContext();
+
+ // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
+ const size_t moveCount = moves.size();
+ for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
+ {
+ const VmaDefragmentationMove& move = moves[moveIndex];
+ pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
+ pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
+ }
+
+ VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
+
+ // Go over all blocks. Create and bind buffer for whole block if necessary.
+ {
+ VkBufferCreateInfo bufCreateInfo = {};
+ bufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
+ VK_BUFFER_USAGE_TRANSFER_DST_BIT;
+
+ for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
+ {
+ VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex];
+ VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
+ if((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0)
+ {
+ bufCreateInfo.size = pBlock->m_pMetadata->GetSize();
+ pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)(
+ m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer);
+ if(pDefragCtx->res == VK_SUCCESS)
+ {
+ pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)(
+ m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0);
+ }
+ }
+ }
+ }
+
+ // Go over all moves. Post data transfer commands to command buffer.
+ if(pDefragCtx->res == VK_SUCCESS)
+ {
+ /*const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
+ VkMappedMemoryRange memRange = {};
+ memRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;*/
+
+ for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
+ {
+ const VmaDefragmentationMove& move = moves[moveIndex];
+
+ const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex];
+ const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex];
+
+ VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer);
+
+ VkBufferCopy region = {
+ move.srcOffset,
+ move.dstOffset,
+ move.size };
+ (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)(
+ commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, &region);
+ }
+ }
+
+ // Save buffers to defrag context for later destruction.
+ if(pDefragCtx->res == VK_SUCCESS && moveCount > 0)
+ {
+ pDefragCtx->res = VK_NOT_READY;
+ }
+}
+
+void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
+{
+ m_HasEmptyBlock = false;
+ for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
+ {
+ VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
+ if(pBlock->m_pMetadata->IsEmpty())
+ {
+ if(m_Blocks.size() > m_MinBlockCount)
+ {
+ if(pDefragmentationStats != VMA_NULL)
+ {
+ ++pDefragmentationStats->deviceMemoryBlocksFreed;
+ pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize();
+ }
+
+ VmaVectorRemove(m_Blocks, blockIndex);
+ pBlock->Destroy(m_hAllocator);
+ vma_delete(m_hAllocator, pBlock);
+ }
+ else
+ {
+ m_HasEmptyBlock = true;
+ }
+ }
+ }
+}
+
+#if VMA_STATS_STRING_ENABLED
+
+void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
+{
+ VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
+
+ json.BeginObject();
+
+ if(m_IsCustomPool)
+ {
+ json.WriteString("MemoryTypeIndex");
+ json.WriteNumber(m_MemoryTypeIndex);
+
+ json.WriteString("BlockSize");
+ json.WriteNumber(m_PreferredBlockSize);
+
+ json.WriteString("BlockCount");
+ json.BeginObject(true);
+ if(m_MinBlockCount > 0)
+ {
+ json.WriteString("Min");
+ json.WriteNumber((uint64_t)m_MinBlockCount);
+ }
+ if(m_MaxBlockCount < SIZE_MAX)
+ {
+ json.WriteString("Max");
+ json.WriteNumber((uint64_t)m_MaxBlockCount);
+ }
+ json.WriteString("Cur");
+ json.WriteNumber((uint64_t)m_Blocks.size());
+ json.EndObject();
+
+ if(m_FrameInUseCount > 0)
+ {
+ json.WriteString("FrameInUseCount");
+ json.WriteNumber(m_FrameInUseCount);
+ }
+
+ if(m_Algorithm != 0)
+ {
+ json.WriteString("Algorithm");
+ json.WriteString(VmaAlgorithmToStr(m_Algorithm));
+ }
+ }
+ else
+ {
+ json.WriteString("PreferredBlockSize");
+ json.WriteNumber(m_PreferredBlockSize);
+ }
+
+ json.WriteString("Blocks");
+ json.BeginObject();
+ for(size_t i = 0; i < m_Blocks.size(); ++i)
+ {
+ json.BeginString();
+ json.ContinueString(m_Blocks[i]->GetId());
+ json.EndString();
+
+ m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
+ }
+ json.EndObject();
+
+ json.EndObject();
+}
+
+#endif // #if VMA_STATS_STRING_ENABLED
+
+void VmaBlockVector::Defragment(
+ class VmaBlockVectorDefragmentationContext* pCtx,
+ VmaDefragmentationStats* pStats,
+ VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
+ VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
+ VkCommandBuffer commandBuffer)
+{
+ pCtx->res = VK_SUCCESS;
+
+ const VkMemoryPropertyFlags memPropFlags =
+ m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags;
+ const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
+ const bool isHostCoherent = (memPropFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0;
+
+ const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 &&
+ isHostVisible;
+ const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 &&
+ (VMA_DEBUG_DETECT_CORRUPTION == 0 || !(isHostVisible && isHostCoherent));
+
+ // There are options to defragment this memory type.
+ if(canDefragmentOnCpu || canDefragmentOnGpu)
+ {
+ bool defragmentOnGpu;
+ // There is only one option to defragment this memory type.
+ if(canDefragmentOnGpu != canDefragmentOnCpu)
+ {
+ defragmentOnGpu = canDefragmentOnGpu;
+ }
+ // Both options are available: Heuristics to choose the best one.
+ else
+ {
+ defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 ||
+ m_hAllocator->IsIntegratedGpu();
+ }
+
+ bool overlappingMoveSupported = !defragmentOnGpu;
+
+ if(m_hAllocator->m_UseMutex)
+ {
+ m_Mutex.LockWrite();
+ pCtx->mutexLocked = true;
+ }
+
+ pCtx->Begin(overlappingMoveSupported);
+
+ // Defragment.
+
+ const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove;
+ const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove;
+ VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> > moves =
+ VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >(VmaStlAllocator<VmaDefragmentationMove>(m_hAllocator->GetAllocationCallbacks()));
+ pCtx->res = pCtx->GetAlgorithm()->Defragment(moves, maxBytesToMove, maxAllocationsToMove);
+
+ // Accumulate statistics.
+ if(pStats != VMA_NULL)
+ {
+ const VkDeviceSize bytesMoved = pCtx->GetAlgorithm()->GetBytesMoved();
+ const uint32_t allocationsMoved = pCtx->GetAlgorithm()->GetAllocationsMoved();
+ pStats->bytesMoved += bytesMoved;
+ pStats->allocationsMoved += allocationsMoved;
+ VMA_ASSERT(bytesMoved <= maxBytesToMove);
+ VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
+ if(defragmentOnGpu)
+ {
+ maxGpuBytesToMove -= bytesMoved;
+ maxGpuAllocationsToMove -= allocationsMoved;
+ }
+ else
+ {
+ maxCpuBytesToMove -= bytesMoved;
+ maxCpuAllocationsToMove -= allocationsMoved;
+ }
+ }
+
+ if(pCtx->res >= VK_SUCCESS)
+ {
+ if(defragmentOnGpu)
+ {
+ ApplyDefragmentationMovesGpu(pCtx, moves, commandBuffer);
+ }
+ else
+ {
+ ApplyDefragmentationMovesCpu(pCtx, moves);
+ }
+ }
+ }
+}
+
+void VmaBlockVector::DefragmentationEnd(
+ class VmaBlockVectorDefragmentationContext* pCtx,
+ VmaDefragmentationStats* pStats)
+{
+ // Destroy buffers.
+ for(size_t blockIndex = pCtx->blockContexts.size(); blockIndex--; )
+ {
+ VmaBlockDefragmentationContext& blockCtx = pCtx->blockContexts[blockIndex];
+ if(blockCtx.hBuffer)
+ {
+ (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)(
+ m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks());
+ }
+ }
+
+ if(pCtx->res >= VK_SUCCESS)
+ {
+ FreeEmptyBlocks(pStats);
+ }
+
+ if(pCtx->mutexLocked)
+ {
+ VMA_ASSERT(m_hAllocator->m_UseMutex);
+ m_Mutex.UnlockWrite();
+ }
+}
+
+size_t VmaBlockVector::CalcAllocationCount() const
+{
+ size_t result = 0;
+ for(size_t i = 0; i < m_Blocks.size(); ++i)
+ {
+ result += m_Blocks[i]->m_pMetadata->GetAllocationCount();
+ }
+ return result;
+}
+
+bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const
+{
+ if(m_BufferImageGranularity == 1)
+ {
+ return false;
+ }
+ VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE;
+ for(size_t i = 0, count = m_Blocks.size(); i < count; ++i)
+ {
+ VmaDeviceMemoryBlock* const pBlock = m_Blocks[i];
+ VMA_ASSERT(m_Algorithm == 0);
+ VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata;
+ if(pMetadata->IsBufferImageGranularityConflictPossible(m_BufferImageGranularity, lastSuballocType))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+void VmaBlockVector::MakePoolAllocationsLost(
+ uint32_t currentFrameIndex,
+ size_t* pLostAllocationCount)
+{
+ VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
+ size_t lostAllocationCount = 0;
+ for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
+ {
+ VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
+ VMA_ASSERT(pBlock);
+ lostAllocationCount += pBlock->m_pMetadata->MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount);
+ }
+ if(pLostAllocationCount != VMA_NULL)
+ {
+ *pLostAllocationCount = lostAllocationCount;
+ }
+}
+
+VkResult VmaBlockVector::CheckCorruption()
+{
+ if(!IsCorruptionDetectionEnabled())
+ {
+ return VK_ERROR_FEATURE_NOT_PRESENT;
+ }
+
+ VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
+ for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
+ {
+ VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
+ VMA_ASSERT(pBlock);
+ VkResult res = pBlock->CheckCorruption(m_hAllocator);
+ if(res != VK_SUCCESS)
+ {
+ return res;
+ }
+ }
+ return VK_SUCCESS;
+}
+
+void VmaBlockVector::AddStats(VmaStats* pStats)
+{
+ const uint32_t memTypeIndex = m_MemoryTypeIndex;
+ const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
+
+ VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
+
+ for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
+ {
+ const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
+ VMA_ASSERT(pBlock);
+ VMA_HEAVY_ASSERT(pBlock->Validate());
+ VmaStatInfo allocationStatInfo;
+ pBlock->m_pMetadata->CalcAllocationStatInfo(allocationStatInfo);
+ VmaAddStatInfo(pStats->total, allocationStatInfo);
+ VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
+ VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// VmaDefragmentationAlgorithm_Generic members definition
+
+VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic(
+ VmaAllocator hAllocator,
+ VmaBlockVector* pBlockVector,
+ uint32_t currentFrameIndex,
+ bool /*overlappingMoveSupported*/) :
+ VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
+ m_AllocationCount(0),
+ m_AllAllocations(false),
+ m_BytesMoved(0),
+ m_AllocationsMoved(0),
+ m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
+{
+ // Create block info for each block.
+ const size_t blockCount = m_pBlockVector->m_Blocks.size();
+ for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
+ {
+ BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
+ pBlockInfo->m_OriginalBlockIndex = blockIndex;
+ pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
+ m_Blocks.push_back(pBlockInfo);
+ }
+
+ // Sort them by m_pBlock pointer value.
+ VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
+}
+
+VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic()
+{
+ for(size_t i = m_Blocks.size(); i--; )
+ {
+ vma_delete(m_hAllocator, m_Blocks[i]);
+ }
+}
+
+void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
+{
+ // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
+ if(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
+ {
+ VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock();
+ BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
+ if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
+ {
+ AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged);
+ (*it)->m_Allocations.push_back(allocInfo);
+ }
+ else
+ {
+ VMA_ASSERT(0);
+ }
+
+ ++m_AllocationCount;
+ }
+}
+
+VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound(
+ VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
+ VkDeviceSize maxBytesToMove,
+ uint32_t maxAllocationsToMove)
+{
+ if(m_Blocks.empty())
+ {
+ return VK_SUCCESS;
+ }
+
+ // This is a choice based on research.
+ // Option 1:
+ uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT;
+ // Option 2:
+ //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
+ // Option 3:
+ //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT;
+
+ size_t srcBlockMinIndex = 0;
+ // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations.
+ /*
+ if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT)
+ {
+ const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount();
+ if(blocksWithNonMovableCount > 0)
+ {
+ srcBlockMinIndex = blocksWithNonMovableCount - 1;
+ }
+ }
+ */
+
+ size_t srcBlockIndex = m_Blocks.size() - 1;
+ size_t srcAllocIndex = SIZE_MAX;
+ for(;;)
+ {
+ // 1. Find next allocation to move.
+ // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
+ // 1.2. Then start from last to first m_Allocations.
+ while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
+ {
+ if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
+ {
+ // Finished: no more allocations to process.
+ if(srcBlockIndex == srcBlockMinIndex)
+ {
+ return VK_SUCCESS;
+ }
+ else
+ {
+ --srcBlockIndex;
+ srcAllocIndex = SIZE_MAX;
+ }
+ }
+ else
+ {
+ srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
+ }
+ }
+
+ BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
+ AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
+
+ const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
+ const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
+ const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
+ const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
+
+ // 2. Try to find new place for this allocation in preceding or current block.
+ for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
+ {
+ BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
+ VmaAllocationRequest dstAllocRequest;
+ if(pDstBlockInfo->m_pBlock->m_pMetadata->CreateAllocationRequest(
+ m_CurrentFrameIndex,
+ m_pBlockVector->GetFrameInUseCount(),
+ m_pBlockVector->GetBufferImageGranularity(),
+ size,
+ alignment,
+ false, // upperAddress
+ suballocType,
+ false, // canMakeOtherLost
+ strategy,
+ &dstAllocRequest) &&
+ MoveMakesSense(
+ dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
+ {
+ VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
+
+ // Reached limit on number of allocations or bytes to move.
+ if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
+ (m_BytesMoved + size > maxBytesToMove))
+ {
+ return VK_SUCCESS;
+ }
+
+ VmaDefragmentationMove move;
+ move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex;
+ move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex;
+ move.srcOffset = srcOffset;
+ move.dstOffset = dstAllocRequest.offset;
+ move.size = size;
+ moves.push_back(move);
+
+ pDstBlockInfo->m_pBlock->m_pMetadata->Alloc(
+ dstAllocRequest,
+ suballocType,
+ size,
+ false, // upperAddress
+ allocInfo.m_hAllocation);
+ pSrcBlockInfo->m_pBlock->m_pMetadata->FreeAtOffset(srcOffset);
+
+ allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
+
+ if(allocInfo.m_pChanged != VMA_NULL)
+ {
+ *allocInfo.m_pChanged = VK_TRUE;
+ }
+
+ ++m_AllocationsMoved;
+ m_BytesMoved += size;
+
+ VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
+
+ break;
+ }
+ }
+
+ // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
+
+ if(srcAllocIndex > 0)
+ {
+ --srcAllocIndex;
+ }
+ else
+ {
+ if(srcBlockIndex > 0)
+ {
+ --srcBlockIndex;
+ srcAllocIndex = SIZE_MAX;
+ }
+ else
+ {
+ return VK_SUCCESS;
+ }
+ }
+ }
+}
+
+size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const
+{
+ size_t result = 0;
+ for(size_t i = 0; i < m_Blocks.size(); ++i)
+ {
+ if(m_Blocks[i]->m_HasNonMovableAllocations)
+ {
+ ++result;
+ }
+ }
+ return result;
+}
+
+VkResult VmaDefragmentationAlgorithm_Generic::Defragment(
+ VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
+ VkDeviceSize maxBytesToMove,
+ uint32_t maxAllocationsToMove)
+{
+ if(!m_AllAllocations && m_AllocationCount == 0)
+ {
+ return VK_SUCCESS;
+ }
+
+ const size_t blockCount = m_Blocks.size();
+ for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
+ {
+ BlockInfo* pBlockInfo = m_Blocks[blockIndex];
+
+ if(m_AllAllocations)
+ {
+ VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata;
+ for(VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin();
+ it != pMetadata->m_Suballocations.end();
+ ++it)
+ {
+ if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ AllocationInfo allocInfo = AllocationInfo(it->hAllocation, VMA_NULL);
+ pBlockInfo->m_Allocations.push_back(allocInfo);
+ }
+ }
+ }
+
+ pBlockInfo->CalcHasNonMovableAllocations();
+
+ // This is a choice based on research.
+ // Option 1:
+ pBlockInfo->SortAllocationsByOffsetDescending();
+ // Option 2:
+ //pBlockInfo->SortAllocationsBySizeDescending();
+ }
+
+ // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
+ VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
+
+ // This is a choice based on research.
+ const uint32_t roundCount = 2;
+
+ // Execute defragmentation rounds (the main part).
+ VkResult result = VK_SUCCESS;
+ for(uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round)
+ {
+ result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove);
+ }
+
+ return result;
+}
+
+bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense(
+ size_t dstBlockIndex, VkDeviceSize dstOffset,
+ size_t srcBlockIndex, VkDeviceSize srcOffset)
+{
+ if(dstBlockIndex < srcBlockIndex)
+ {
+ return true;
+ }
+ if(dstBlockIndex > srcBlockIndex)
+ {
+ return false;
+ }
+ if(dstOffset < srcOffset)
+ {
+ return true;
+ }
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// VmaDefragmentationAlgorithm_Fast
+
+VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast(
+ VmaAllocator hAllocator,
+ VmaBlockVector* pBlockVector,
+ uint32_t currentFrameIndex,
+ bool overlappingMoveSupported) :
+ VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
+ m_OverlappingMoveSupported(overlappingMoveSupported),
+ m_AllocationCount(0),
+ m_AllAllocations(false),
+ m_BytesMoved(0),
+ m_AllocationsMoved(0),
+ m_BlockInfos(VmaStlAllocator<BlockInfo>(hAllocator->GetAllocationCallbacks()))
+{
+ VMA_ASSERT(VMA_DEBUG_MARGIN == 0);
+
+}
+
+VmaDefragmentationAlgorithm_Fast::~VmaDefragmentationAlgorithm_Fast()
+{
+}
+
+VkResult VmaDefragmentationAlgorithm_Fast::Defragment(
+ VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
+ VkDeviceSize maxBytesToMove,
+ uint32_t maxAllocationsToMove)
+{
+ VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount);
+
+ const size_t blockCount = m_pBlockVector->GetBlockCount();
+ if(blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0)
+ {
+ return VK_SUCCESS;
+ }
+
+ PreprocessMetadata();
+
+ // Sort blocks in order from most destination.
+
+ m_BlockInfos.resize(blockCount);
+ for(size_t i = 0; i < blockCount; ++i)
+ {
+ m_BlockInfos[i].origBlockIndex = i;
+ }
+
+ VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool {
+ return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() <
+ m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize();
+ });
+
+ // THE MAIN ALGORITHM
+
+ FreeSpaceDatabase freeSpaceDb;
+
+ size_t dstBlockInfoIndex = 0;
+ size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
+ VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
+ VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
+ VkDeviceSize dstBlockSize = pDstMetadata->GetSize();
+ VkDeviceSize dstOffset = 0;
+
+ bool end = false;
+ for(size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex)
+ {
+ const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex;
+ VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(srcOrigBlockIndex);
+ VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata;
+ for(VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin();
+ !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); )
+ {
+ VmaAllocation_T* const pAlloc = srcSuballocIt->hAllocation;
+ const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment();
+ const VkDeviceSize srcAllocSize = srcSuballocIt->size;
+ if(m_AllocationsMoved == maxAllocationsToMove ||
+ m_BytesMoved + srcAllocSize > maxBytesToMove)
+ {
+ end = true;
+ break;
+ }
+ const VkDeviceSize srcAllocOffset = srcSuballocIt->offset;
+
+ // Try to place it in one of free spaces from the database.
+ size_t freeSpaceInfoIndex;
+ VkDeviceSize dstAllocOffset;
+ if(freeSpaceDb.Fetch(srcAllocAlignment, srcAllocSize,
+ freeSpaceInfoIndex, dstAllocOffset))
+ {
+ size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex;
+ VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex);
+ VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata;
+ /*VkDeviceSize freeSpaceBlockSize = pFreeSpaceMetadata->GetSize();*/
+
+ // Same block
+ if(freeSpaceInfoIndex == srcBlockInfoIndex)
+ {
+ VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
+
+ // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
+
+ VmaSuballocation suballoc = *srcSuballocIt;
+ suballoc.offset = dstAllocOffset;
+ suballoc.hAllocation->ChangeOffset(dstAllocOffset);
+ m_BytesMoved += srcAllocSize;
+ ++m_AllocationsMoved;
+
+ VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
+ ++nextSuballocIt;
+ pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
+ srcSuballocIt = nextSuballocIt;
+
+ InsertSuballoc(pFreeSpaceMetadata, suballoc);
+
+ VmaDefragmentationMove move = {
+ srcOrigBlockIndex, freeSpaceOrigBlockIndex,
+ srcAllocOffset, dstAllocOffset,
+ srcAllocSize };
+ moves.push_back(move);
+ }
+ // Different block
+ else
+ {
+ // MOVE OPTION 2: Move the allocation to a different block.
+
+ VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex);
+
+ VmaSuballocation suballoc = *srcSuballocIt;
+ suballoc.offset = dstAllocOffset;
+ suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, dstAllocOffset);
+ m_BytesMoved += srcAllocSize;
+ ++m_AllocationsMoved;
+
+ VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
+ ++nextSuballocIt;
+ pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
+ srcSuballocIt = nextSuballocIt;
+
+ InsertSuballoc(pFreeSpaceMetadata, suballoc);
+
+ VmaDefragmentationMove move = {
+ srcOrigBlockIndex, freeSpaceOrigBlockIndex,
+ srcAllocOffset, dstAllocOffset,
+ srcAllocSize };
+ moves.push_back(move);
+ }
+ }
+ else
+ {
+ dstAllocOffset = VmaAlignUp(dstOffset, srcAllocAlignment);
+
+ // If the allocation doesn't fit before the end of dstBlock, forward to next block.
+ while(dstBlockInfoIndex < srcBlockInfoIndex &&
+ dstAllocOffset + srcAllocSize > dstBlockSize)
+ {
+ // But before that, register remaining free space at the end of dst block.
+ freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, dstBlockSize - dstOffset);
+
+ ++dstBlockInfoIndex;
+ dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
+ pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
+ pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
+ dstBlockSize = pDstMetadata->GetSize();
+ dstOffset = 0;
+ dstAllocOffset = 0;
+ }
+
+ // Same block
+ if(dstBlockInfoIndex == srcBlockInfoIndex)
+ {
+ VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
+
+ const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset;
+
+ bool skipOver = overlap;
+ if(overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset)
+ {
+ // If destination and source place overlap, skip if it would move it
+ // by only < 1/64 of its size.
+ skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize;
+ }
+
+ if(skipOver)
+ {
+ freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, srcAllocOffset - dstOffset);
+
+ dstOffset = srcAllocOffset + srcAllocSize;
+ ++srcSuballocIt;
+ }
+ // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
+ else
+ {
+ srcSuballocIt->offset = dstAllocOffset;
+ srcSuballocIt->hAllocation->ChangeOffset(dstAllocOffset);
+ dstOffset = dstAllocOffset + srcAllocSize;
+ m_BytesMoved += srcAllocSize;
+ ++m_AllocationsMoved;
+ ++srcSuballocIt;
+ VmaDefragmentationMove move = {
+ srcOrigBlockIndex, dstOrigBlockIndex,
+ srcAllocOffset, dstAllocOffset,
+ srcAllocSize };
+ moves.push_back(move);
+ }
+ }
+ // Different block
+ else
+ {
+ // MOVE OPTION 2: Move the allocation to a different block.
+
+ VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex);
+ VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize);
+
+ VmaSuballocation suballoc = *srcSuballocIt;
+ suballoc.offset = dstAllocOffset;
+ suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlock, dstAllocOffset);
+ dstOffset = dstAllocOffset + srcAllocSize;
+ m_BytesMoved += srcAllocSize;
+ ++m_AllocationsMoved;
+
+ VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
+ ++nextSuballocIt;
+ pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
+ srcSuballocIt = nextSuballocIt;
+
+ pDstMetadata->m_Suballocations.push_back(suballoc);
+
+ VmaDefragmentationMove move = {
+ srcOrigBlockIndex, dstOrigBlockIndex,
+ srcAllocOffset, dstAllocOffset,
+ srcAllocSize };
+ moves.push_back(move);
+ }
+ }
+ }
+ }
+
+ m_BlockInfos.clear();
+
+ PostprocessMetadata();
+
+ return VK_SUCCESS;
+}
+
+void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata()
+{
+ const size_t blockCount = m_pBlockVector->GetBlockCount();
+ for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
+ {
+ VmaBlockMetadata_Generic* const pMetadata =
+ (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
+ pMetadata->m_FreeCount = 0;
+ pMetadata->m_SumFreeSize = pMetadata->GetSize();
+ pMetadata->m_FreeSuballocationsBySize.clear();
+ for(VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
+ it != pMetadata->m_Suballocations.end(); )
+ {
+ if(it->type == VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ VmaSuballocationList::iterator nextIt = it;
+ ++nextIt;
+ pMetadata->m_Suballocations.erase(it);
+ it = nextIt;
+ }
+ else
+ {
+ ++it;
+ }
+ }
+ }
+}
+
+void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata()
+{
+ const size_t blockCount = m_pBlockVector->GetBlockCount();
+ for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
+ {
+ VmaBlockMetadata_Generic* const pMetadata =
+ (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
+ const VkDeviceSize blockSize = pMetadata->GetSize();
+
+ // No allocations in this block - entire area is free.
+ if(pMetadata->m_Suballocations.empty())
+ {
+ pMetadata->m_FreeCount = 1;
+ //pMetadata->m_SumFreeSize is already set to blockSize.
+ VmaSuballocation suballoc = {
+ 0, // offset
+ blockSize, // size
+ VMA_NULL, // hAllocation
+ VMA_SUBALLOCATION_TYPE_FREE };
+ pMetadata->m_Suballocations.push_back(suballoc);
+ pMetadata->RegisterFreeSuballocation(pMetadata->m_Suballocations.begin());
+ }
+ // There are some allocations in this block.
+ else
+ {
+ VkDeviceSize offset = 0;
+ VmaSuballocationList::iterator it;
+ for(it = pMetadata->m_Suballocations.begin();
+ it != pMetadata->m_Suballocations.end();
+ ++it)
+ {
+ VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE);
+ VMA_ASSERT(it->offset >= offset);
+
+ // Need to insert preceding free space.
+ if(it->offset > offset)
+ {
+ ++pMetadata->m_FreeCount;
+ const VkDeviceSize freeSize = it->offset - offset;
+ VmaSuballocation suballoc = {
+ offset, // offset
+ freeSize, // size
+ VMA_NULL, // hAllocation
+ VMA_SUBALLOCATION_TYPE_FREE };
+ VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
+ if(freeSize >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
+ {
+ pMetadata->m_FreeSuballocationsBySize.push_back(precedingFreeIt);
+ }
+ }
+
+ pMetadata->m_SumFreeSize -= it->size;
+ offset = it->offset + it->size;
+ }
+
+ // Need to insert trailing free space.
+ if(offset < blockSize)
+ {
+ ++pMetadata->m_FreeCount;
+ const VkDeviceSize freeSize = blockSize - offset;
+ VmaSuballocation suballoc = {
+ offset, // offset
+ freeSize, // size
+ VMA_NULL, // hAllocation
+ VMA_SUBALLOCATION_TYPE_FREE };
+ VMA_ASSERT(it == pMetadata->m_Suballocations.end());
+ VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
+ if(freeSize > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
+ {
+ pMetadata->m_FreeSuballocationsBySize.push_back(trailingFreeIt);
+ }
+ }
+
+ VMA_SORT(
+ pMetadata->m_FreeSuballocationsBySize.begin(),
+ pMetadata->m_FreeSuballocationsBySize.end(),
+ VmaSuballocationItemSizeLess());
+ }
+
+ VMA_HEAVY_ASSERT(pMetadata->Validate());
+ }
+}
+
+void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc)
+{
+ // TODO: Optimize somehow. Remember iterator instead of searching for it linearly.
+ VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
+ while(it != pMetadata->m_Suballocations.end())
+ {
+ if(it->offset < suballoc.offset)
+ {
+ ++it;
+ }
+ }
+ pMetadata->m_Suballocations.insert(it, suballoc);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// VmaBlockVectorDefragmentationContext
+
+VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext(
+ VmaAllocator hAllocator,
+ VmaPool hCustomPool,
+ VmaBlockVector* pBlockVector,
+ uint32_t currFrameIndex,
+ uint32_t /*algorithmFlags*/) :
+ res(VK_SUCCESS),
+ mutexLocked(false),
+ blockContexts(VmaStlAllocator<VmaBlockDefragmentationContext>(hAllocator->GetAllocationCallbacks())),
+ m_hAllocator(hAllocator),
+ m_hCustomPool(hCustomPool),
+ m_pBlockVector(pBlockVector),
+ m_CurrFrameIndex(currFrameIndex),
+ /*m_AlgorithmFlags(algorithmFlags),*/
+ m_pAlgorithm(VMA_NULL),
+ m_Allocations(VmaStlAllocator<AllocInfo>(hAllocator->GetAllocationCallbacks())),
+ m_AllAllocations(false)
+{
+}
+
+VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext()
+{
+ vma_delete(m_hAllocator, m_pAlgorithm);
+}
+
+void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
+{
+ AllocInfo info = { hAlloc, pChanged };
+ m_Allocations.push_back(info);
+}
+
+void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported)
+{
+ const bool allAllocations = m_AllAllocations ||
+ m_Allocations.size() == m_pBlockVector->CalcAllocationCount();
+
+ /********************************
+ HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM.
+ ********************************/
+
+ /*
+ Fast algorithm is supported only when certain criteria are met:
+ - VMA_DEBUG_MARGIN is 0.
+ - All allocations in this block vector are moveable.
+ - There is no possibility of image/buffer granularity conflict.
+ */
+ if(VMA_DEBUG_MARGIN == 0 &&
+ allAllocations &&
+ !m_pBlockVector->IsBufferImageGranularityConflictPossible())
+ {
+ m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)(
+ m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
+ }
+ else
+ {
+ m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)(
+ m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
+ }
+
+ if(allAllocations)
+ {
+ m_pAlgorithm->AddAll();
+ }
+ else
+ {
+ for(size_t i = 0, count = m_Allocations.size(); i < count; ++i)
+ {
+ m_pAlgorithm->AddAllocation(m_Allocations[i].hAlloc, m_Allocations[i].pChanged);
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// VmaDefragmentationContext
+
+VmaDefragmentationContext_T::VmaDefragmentationContext_T(
+ VmaAllocator hAllocator,
+ uint32_t currFrameIndex,
+ uint32_t flags,
+ VmaDefragmentationStats* pStats) :
+ m_hAllocator(hAllocator),
+ m_CurrFrameIndex(currFrameIndex),
+ m_Flags(flags),
+ m_pStats(pStats),
+ m_CustomPoolContexts(VmaStlAllocator<VmaBlockVectorDefragmentationContext*>(hAllocator->GetAllocationCallbacks()))
+{
+ memset(m_DefaultPoolContexts, 0, sizeof(m_DefaultPoolContexts));
+}
+
+VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
+{
+ for(size_t i = m_CustomPoolContexts.size(); i--; )
+ {
+ VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i];
+ pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_pStats);
+ vma_delete(m_hAllocator, pBlockVectorCtx);
+ }
+ for(size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; )
+ {
+ VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i];
+ if(pBlockVectorCtx)
+ {
+ pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_pStats);
+ vma_delete(m_hAllocator, pBlockVectorCtx);
+ }
+ }
+}
+
+void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, VmaPool* pPools)
+{
+ for(uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
+ {
+ VmaPool pool = pPools[poolIndex];
+ VMA_ASSERT(pool);
+ // Pools with algorithm other than default are not defragmented.
+ if(pool->m_BlockVector.GetAlgorithm() == 0)
+ {
+ VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
+
+ for(size_t i = m_CustomPoolContexts.size(); i--; )
+ {
+ if(m_CustomPoolContexts[i]->GetCustomPool() == pool)
+ {
+ pBlockVectorDefragCtx = m_CustomPoolContexts[i];
+ break;
+ }
+ }
+
+ if(!pBlockVectorDefragCtx)
+ {
+ pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
+ m_hAllocator,
+ pool,
+ &pool->m_BlockVector,
+ m_CurrFrameIndex,
+ m_Flags);
+ m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
+ }
+
+ pBlockVectorDefragCtx->AddAll();
+ }
+ }
+}
+
+void VmaDefragmentationContext_T::AddAllocations(
+ uint32_t allocationCount,
+ VmaAllocation* pAllocations,
+ VkBool32* pAllocationsChanged)
+{
+ // Dispatch pAllocations among defragmentators. Create them when necessary.
+ for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
+ {
+ const VmaAllocation hAlloc = pAllocations[allocIndex];
+ VMA_ASSERT(hAlloc);
+ // DedicatedAlloc cannot be defragmented.
+ if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
+ // Lost allocation cannot be defragmented.
+ (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
+ {
+ VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
+
+ const VmaPool hAllocPool = hAlloc->GetPool();
+ // This allocation belongs to custom pool.
+ if(hAllocPool != VK_NULL_HANDLE)
+ {
+ // Pools with algorithm other than default are not defragmented.
+ if(hAllocPool->m_BlockVector.GetAlgorithm() == 0)
+ {
+ for(size_t i = m_CustomPoolContexts.size(); i--; )
+ {
+ if(m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool)
+ {
+ pBlockVectorDefragCtx = m_CustomPoolContexts[i];
+ break;
+ }
+ }
+ if(!pBlockVectorDefragCtx)
+ {
+ pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
+ m_hAllocator,
+ hAllocPool,
+ &hAllocPool->m_BlockVector,
+ m_CurrFrameIndex,
+ m_Flags);
+ m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
+ }
+ }
+ }
+ // This allocation belongs to default pool.
+ else
+ {
+ const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
+ pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex];
+ if(!pBlockVectorDefragCtx)
+ {
+ pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
+ m_hAllocator,
+ VMA_NULL, // hCustomPool
+ m_hAllocator->m_pBlockVectors[memTypeIndex],
+ m_CurrFrameIndex,
+ m_Flags);
+ m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx;
+ }
+ }
+
+ if(pBlockVectorDefragCtx)
+ {
+ VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
+ &pAllocationsChanged[allocIndex] : VMA_NULL;
+ pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged);
+ }
+ }
+ }
+}
+
+VkResult VmaDefragmentationContext_T::Defragment(
+ VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
+ VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
+ VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats)
+{
+ if(pStats)
+ {
+ memset(pStats, 0, sizeof(VmaDefragmentationStats));
+ }
+
+ if(commandBuffer == VK_NULL_HANDLE)
+ {
+ maxGpuBytesToMove = 0;
+ maxGpuAllocationsToMove = 0;
+ }
+
+ VkResult res = VK_SUCCESS;
+
+ // Process default pools.
+ for(uint32_t memTypeIndex = 0;
+ memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS;
+ ++memTypeIndex)
+ {
+ VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
+ if(pBlockVectorCtx)
+ {
+ VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
+ pBlockVectorCtx->GetBlockVector()->Defragment(
+ pBlockVectorCtx,
+ pStats,
+ maxCpuBytesToMove, maxCpuAllocationsToMove,
+ maxGpuBytesToMove, maxGpuAllocationsToMove,
+ commandBuffer);
+ if(pBlockVectorCtx->res != VK_SUCCESS)
+ {
+ res = pBlockVectorCtx->res;
+ }
+ }
+ }
+
+ // Process custom pools.
+ for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
+ customCtxIndex < customCtxCount && res >= VK_SUCCESS;
+ ++customCtxIndex)
+ {
+ VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
+ VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
+ pBlockVectorCtx->GetBlockVector()->Defragment(
+ pBlockVectorCtx,
+ pStats,
+ maxCpuBytesToMove, maxCpuAllocationsToMove,
+ maxGpuBytesToMove, maxGpuAllocationsToMove,
+ commandBuffer);
+ if(pBlockVectorCtx->res != VK_SUCCESS)
+ {
+ res = pBlockVectorCtx->res;
+ }
+ }
+
+ return res;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// VmaRecorder
+
+#if VMA_RECORDING_ENABLED
+
+VmaRecorder::VmaRecorder() :
+ m_UseMutex(true),
+ m_Flags(0),
+ m_File(VMA_NULL),
+ m_Freq(INT64_MAX),
+ m_StartCounter(INT64_MAX)
+{
+}
+
+VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex)
+{
+ m_UseMutex = useMutex;
+ m_Flags = settings.flags;
+
+ QueryPerformanceFrequency((LARGE_INTEGER*)&m_Freq);
+ QueryPerformanceCounter((LARGE_INTEGER*)&m_StartCounter);
+
+ // Open file for writing.
+ errno_t err = fopen_s(&m_File, settings.pFilePath, "wb");
+ if(err != 0)
+ {
+ return VK_ERROR_INITIALIZATION_FAILED;
+ }
+
+ // Write header.
+ fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");
+ fprintf(m_File, "%s\n", "1,5");
+
+ return VK_SUCCESS;
+}
+
+VmaRecorder::~VmaRecorder()
+{
+ if(m_File != VMA_NULL)
+ {
+ fclose(m_File);
+ }
+}
+
+void VmaRecorder::RecordCreateAllocator(uint32_t frameIndex)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaCreateAllocator\n", callParams.threadId, callParams.time, frameIndex);
+ Flush();
+}
+
+void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n", callParams.threadId, callParams.time, frameIndex);
+ Flush();
+}
+
+void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", callParams.threadId, callParams.time, frameIndex,
+ createInfo.memoryTypeIndex,
+ createInfo.flags,
+ createInfo.blockSize,
+ (uint64_t)createInfo.minBlockCount,
+ (uint64_t)createInfo.maxBlockCount,
+ createInfo.frameInUseCount,
+ pool);
+ Flush();
+}
+
+void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", callParams.threadId, callParams.time, frameIndex,
+ pool);
+ Flush();
+}
+
+void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex,
+ const VkMemoryRequirements& vkMemReq,
+ const VmaAllocationCreateInfo& createInfo,
+ VmaAllocation allocation)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
+ fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
+ vkMemReq.size,
+ vkMemReq.alignment,
+ vkMemReq.memoryTypeBits,
+ createInfo.flags,
+ createInfo.usage,
+ createInfo.requiredFlags,
+ createInfo.preferredFlags,
+ createInfo.memoryTypeBits,
+ createInfo.pool,
+ allocation,
+ userDataStr.GetString());
+ Flush();
+}
+
+void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex,
+ const VkMemoryRequirements& vkMemReq,
+ const VmaAllocationCreateInfo& createInfo,
+ uint64_t allocationCount,
+ const VmaAllocation* pAllocations)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
+ fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,", callParams.threadId, callParams.time, frameIndex,
+ vkMemReq.size,
+ vkMemReq.alignment,
+ vkMemReq.memoryTypeBits,
+ createInfo.flags,
+ createInfo.usage,
+ createInfo.requiredFlags,
+ createInfo.preferredFlags,
+ createInfo.memoryTypeBits,
+ createInfo.pool);
+ PrintPointerList(allocationCount, pAllocations);
+ fprintf(m_File, ",%s\n", userDataStr.GetString());
+ Flush();
+}
+
+void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex,
+ const VkMemoryRequirements& vkMemReq,
+ bool requiresDedicatedAllocation,
+ bool prefersDedicatedAllocation,
+ const VmaAllocationCreateInfo& createInfo,
+ VmaAllocation allocation)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
+ fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryForBuffer,%llu,%llu,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
+ vkMemReq.size,
+ vkMemReq.alignment,
+ vkMemReq.memoryTypeBits,
+ requiresDedicatedAllocation ? 1 : 0,
+ prefersDedicatedAllocation ? 1 : 0,
+ createInfo.flags,
+ createInfo.usage,
+ createInfo.requiredFlags,
+ createInfo.preferredFlags,
+ createInfo.memoryTypeBits,
+ createInfo.pool,
+ allocation,
+ userDataStr.GetString());
+ Flush();
+}
+
+void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex,
+ const VkMemoryRequirements& vkMemReq,
+ bool requiresDedicatedAllocation,
+ bool prefersDedicatedAllocation,
+ const VmaAllocationCreateInfo& createInfo,
+ VmaAllocation allocation)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
+ fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryForImage,%llu,%llu,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
+ vkMemReq.size,
+ vkMemReq.alignment,
+ vkMemReq.memoryTypeBits,
+ requiresDedicatedAllocation ? 1 : 0,
+ prefersDedicatedAllocation ? 1 : 0,
+ createInfo.flags,
+ createInfo.usage,
+ createInfo.requiredFlags,
+ createInfo.preferredFlags,
+ createInfo.memoryTypeBits,
+ createInfo.pool,
+ allocation,
+ userDataStr.GetString());
+ Flush();
+}
+
+void VmaRecorder::RecordFreeMemory(uint32_t frameIndex,
+ VmaAllocation allocation)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
+ allocation);
+ Flush();
+}
+
+void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex,
+ uint64_t allocationCount,
+ const VmaAllocation* pAllocations)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages,", callParams.threadId, callParams.time, frameIndex);
+ PrintPointerList(allocationCount, pAllocations);
+ fprintf(m_File, "\n");
+ Flush();
+}
+
+void VmaRecorder::RecordResizeAllocation(
+ uint32_t frameIndex,
+ VmaAllocation allocation,
+ VkDeviceSize newSize)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaResizeAllocation,%p,%llu\n", callParams.threadId, callParams.time, frameIndex,
+ allocation, newSize);
+ Flush();
+}
+
+void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex,
+ VmaAllocation allocation,
+ const void* pUserData)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ UserDataString userDataStr(
+ allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0,
+ pUserData);
+ fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
+ allocation,
+ userDataStr.GetString());
+ Flush();
+}
+
+void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex,
+ VmaAllocation allocation)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
+ allocation);
+ Flush();
+}
+
+void VmaRecorder::RecordMapMemory(uint32_t frameIndex,
+ VmaAllocation allocation)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
+ allocation);
+ Flush();
+}
+
+void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex,
+ VmaAllocation allocation)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
+ allocation);
+ Flush();
+}
+
+void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex,
+ VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
+ allocation,
+ offset,
+ size);
+ Flush();
+}
+
+void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex,
+ VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
+ allocation,
+ offset,
+ size);
+ Flush();
+}
+
+void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex,
+ const VkBufferCreateInfo& bufCreateInfo,
+ const VmaAllocationCreateInfo& allocCreateInfo,
+ VmaAllocation allocation)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
+ fprintf(m_File, "%u,%.3f,%u,vmaCreateBuffer,%u,%llu,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
+ bufCreateInfo.flags,
+ bufCreateInfo.size,
+ bufCreateInfo.usage,
+ bufCreateInfo.sharingMode,
+ allocCreateInfo.flags,
+ allocCreateInfo.usage,
+ allocCreateInfo.requiredFlags,
+ allocCreateInfo.preferredFlags,
+ allocCreateInfo.memoryTypeBits,
+ allocCreateInfo.pool,
+ allocation,
+ userDataStr.GetString());
+ Flush();
+}
+
+void VmaRecorder::RecordCreateImage(uint32_t frameIndex,
+ const VkImageCreateInfo& imageCreateInfo,
+ const VmaAllocationCreateInfo& allocCreateInfo,
+ VmaAllocation allocation)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
+ fprintf(m_File, "%u,%.3f,%u,vmaCreateImage,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
+ imageCreateInfo.flags,
+ imageCreateInfo.imageType,
+ imageCreateInfo.format,
+ imageCreateInfo.extent.width,
+ imageCreateInfo.extent.height,
+ imageCreateInfo.extent.depth,
+ imageCreateInfo.mipLevels,
+ imageCreateInfo.arrayLayers,
+ imageCreateInfo.samples,
+ imageCreateInfo.tiling,
+ imageCreateInfo.usage,
+ imageCreateInfo.sharingMode,
+ imageCreateInfo.initialLayout,
+ allocCreateInfo.flags,
+ allocCreateInfo.usage,
+ allocCreateInfo.requiredFlags,
+ allocCreateInfo.preferredFlags,
+ allocCreateInfo.memoryTypeBits,
+ allocCreateInfo.pool,
+ allocation,
+ userDataStr.GetString());
+ Flush();
+}
+
+void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex,
+ VmaAllocation allocation)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", callParams.threadId, callParams.time, frameIndex,
+ allocation);
+ Flush();
+}
+
+void VmaRecorder::RecordDestroyImage(uint32_t frameIndex,
+ VmaAllocation allocation)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", callParams.threadId, callParams.time, frameIndex,
+ allocation);
+ Flush();
+}
+
+void VmaRecorder::RecordTouchAllocation(uint32_t frameIndex,
+ VmaAllocation allocation)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaTouchAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
+ allocation);
+ Flush();
+}
+
+void VmaRecorder::RecordGetAllocationInfo(uint32_t frameIndex,
+ VmaAllocation allocation)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaGetAllocationInfo,%p\n", callParams.threadId, callParams.time, frameIndex,
+ allocation);
+ Flush();
+}
+
+void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex,
+ VmaPool pool)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaMakePoolAllocationsLost,%p\n", callParams.threadId, callParams.time, frameIndex,
+ pool);
+ Flush();
+}
+
+void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex,
+ const VmaDefragmentationInfo2& info,
+ VmaDefragmentationContext ctx)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u,", callParams.threadId, callParams.time, frameIndex,
+ info.flags);
+ PrintPointerList(info.allocationCount, info.pAllocations);
+ fprintf(m_File, ",");
+ PrintPointerList(info.poolCount, info.pPools);
+ fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n",
+ info.maxCpuBytesToMove,
+ info.maxCpuAllocationsToMove,
+ info.maxGpuBytesToMove,
+ info.maxGpuAllocationsToMove,
+ info.commandBuffer,
+ ctx);
+ Flush();
+}
+
+void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex,
+ VmaDefragmentationContext ctx)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n", callParams.threadId, callParams.time, frameIndex,
+ ctx);
+ Flush();
+}
+
+VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData)
+{
+ if(pUserData != VMA_NULL)
+ {
+ if((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0)
+ {
+ m_Str = (const char*)pUserData;
+ }
+ else
+ {
+ sprintf_s(m_PtrStr, "%p", pUserData);
+ m_Str = m_PtrStr;
+ }
+ }
+ else
+ {
+ m_Str = "";
+ }
+}
+
+void VmaRecorder::WriteConfiguration(
+ const VkPhysicalDeviceProperties& devProps,
+ const VkPhysicalDeviceMemoryProperties& memProps,
+ bool dedicatedAllocationExtensionEnabled)
+{
+ fprintf(m_File, "Config,Begin\n");
+
+ fprintf(m_File, "PhysicalDevice,apiVersion,%u\n", devProps.apiVersion);
+ fprintf(m_File, "PhysicalDevice,driverVersion,%u\n", devProps.driverVersion);
+ fprintf(m_File, "PhysicalDevice,vendorID,%u\n", devProps.vendorID);
+ fprintf(m_File, "PhysicalDevice,deviceID,%u\n", devProps.deviceID);
+ fprintf(m_File, "PhysicalDevice,deviceType,%u\n", devProps.deviceType);
+ fprintf(m_File, "PhysicalDevice,deviceName,%s\n", devProps.deviceName);
+
+ fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n", devProps.limits.maxMemoryAllocationCount);
+ fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n", devProps.limits.bufferImageGranularity);
+ fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n", devProps.limits.nonCoherentAtomSize);
+
+ fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n", memProps.memoryHeapCount);
+ for(uint32_t i = 0; i < memProps.memoryHeapCount; ++i)
+ {
+ fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n", i, memProps.memoryHeaps[i].size);
+ fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n", i, memProps.memoryHeaps[i].flags);
+ }
+ fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n", memProps.memoryTypeCount);
+ for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
+ {
+ fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n", i, memProps.memoryTypes[i].heapIndex);
+ fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n", i, memProps.memoryTypes[i].propertyFlags);
+ }
+
+ fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n", dedicatedAllocationExtensionEnabled ? 1 : 0);
+
+ fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n", VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0);
+ fprintf(m_File, "Macro,VMA_DEBUG_ALIGNMENT,%llu\n", (VkDeviceSize)VMA_DEBUG_ALIGNMENT);
+ fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n", (VkDeviceSize)VMA_DEBUG_MARGIN);
+ fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n", VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0);
+ fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n", VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0);
+ fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n", VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0);
+ fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n", (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY);
+ fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n", (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE);
+ fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n", (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
+
+ fprintf(m_File, "Config,End\n");
+}
+
+void VmaRecorder::GetBasicParams(CallParams& outParams)
+{
+ outParams.threadId = GetCurrentThreadId();
+
+ LARGE_INTEGER counter;
+ QueryPerformanceCounter(&counter);
+ outParams.time = (double)(counter.QuadPart - m_StartCounter) / (double)m_Freq;
+}
+
+void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems)
+{
+ if(count)
+ {
+ fprintf(m_File, "%p", pItems[0]);
+ for(uint64_t i = 1; i < count; ++i)
+ {
+ fprintf(m_File, " %p", pItems[i]);
+ }
+ }
+}
+
+void VmaRecorder::Flush()
+{
+ if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0)
+ {
+ fflush(m_File);
+ }
+}
+
+#endif // #if VMA_RECORDING_ENABLED
+
+////////////////////////////////////////////////////////////////////////////////
+// VmaAllocator_T
+
+VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
+ m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
+ m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
+ m_hDevice(pCreateInfo->device),
+ m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
+ m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
+ *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
+ m_PreferredLargeHeapBlockSize(0),
+ m_PhysicalDevice(pCreateInfo->physicalDevice),
+ m_CurrentFrameIndex(0),
+ m_Pools(VmaStlAllocator<VmaPool>(GetAllocationCallbacks())),
+ m_NextPoolId(0)
+#if VMA_RECORDING_ENABLED
+ ,m_pRecorder(VMA_NULL)
+#endif
+{
+ if(VMA_DEBUG_DETECT_CORRUPTION)
+ {
+ // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
+ VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
+ }
+
+ VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device);
+
+#if !(VMA_DEDICATED_ALLOCATION)
+ if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0)
+ {
+ VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
+ }
+#endif
+
+ memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
+ memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
+ memset(&m_MemProps, 0, sizeof(m_MemProps));
+
+ memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
+ memset(&m_pDedicatedAllocations, 0, sizeof(m_pDedicatedAllocations));
+
+ for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
+ {
+ m_HeapSizeLimit[i] = VK_WHOLE_SIZE;
+ }
+
+ if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
+ {
+ m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
+ m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
+ }
+
+ ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
+
+ (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
+ (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
+
+ VMA_ASSERT(VmaIsPow2(VMA_DEBUG_ALIGNMENT));
+ VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
+ VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
+ VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
+
+ m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
+ pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
+
+ if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
+ {
+ for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
+ {
+ const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
+ if(limit != VK_WHOLE_SIZE)
+ {
+ m_HeapSizeLimit[heapIndex] = limit;
+ if(limit < m_MemProps.memoryHeaps[heapIndex].size)
+ {
+ m_MemProps.memoryHeaps[heapIndex].size = limit;
+ }
+ }
+ }
+ }
+
+ for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
+ {
+ const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
+
+ m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
+ this,
+ memTypeIndex,
+ preferredBlockSize,
+ 0,
+ SIZE_MAX,
+ GetBufferImageGranularity(),
+ pCreateInfo->frameInUseCount,
+ false, // isCustomPool
+ false, // explicitBlockSize
+ false); // linearAlgorithm
+ // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
+ // becase minBlockCount is 0.
+ m_pDedicatedAllocations[memTypeIndex] = vma_new(this, AllocationVectorType)(VmaStlAllocator<VmaAllocation>(GetAllocationCallbacks()));
+
+ }
+}
+
+VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
+{
+ VkResult res = VK_SUCCESS;
+
+ if(pCreateInfo->pRecordSettings != VMA_NULL &&
+ !VmaStrIsEmpty(pCreateInfo->pRecordSettings->pFilePath))
+ {
+#if VMA_RECORDING_ENABLED
+ m_pRecorder = vma_new(this, VmaRecorder)();
+ res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex);
+ if(res != VK_SUCCESS)
+ {
+ return res;
+ }
+ m_pRecorder->WriteConfiguration(
+ m_PhysicalDeviceProperties,
+ m_MemProps,
+ m_UseKhrDedicatedAllocation);
+ m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex());
+#else
+ VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1.");
+ return VK_ERROR_FEATURE_NOT_PRESENT;
+#endif
+ }
+
+ return res;
+}
+
+VmaAllocator_T::~VmaAllocator_T()
+{
+#if VMA_RECORDING_ENABLED
+ if(m_pRecorder != VMA_NULL)
+ {
+ m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex());
+ vma_delete(this, m_pRecorder);
+ }
+#endif
+
+ VMA_ASSERT(m_Pools.empty());
+
+ for(size_t i = GetMemoryTypeCount(); i--; )
+ {
+ vma_delete(this, m_pDedicatedAllocations[i]);
+ vma_delete(this, m_pBlockVectors[i]);
+ }
+}
+
+void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
+{
+#if VMA_STATIC_VULKAN_FUNCTIONS == 1
+ m_VulkanFunctions.vkGetPhysicalDeviceProperties = &vkGetPhysicalDeviceProperties;
+ m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = &vkGetPhysicalDeviceMemoryProperties;
+ m_VulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
+ m_VulkanFunctions.vkFreeMemory = &vkFreeMemory;
+ m_VulkanFunctions.vkMapMemory = &vkMapMemory;
+ m_VulkanFunctions.vkUnmapMemory = &vkUnmapMemory;
+ m_VulkanFunctions.vkFlushMappedMemoryRanges = &vkFlushMappedMemoryRanges;
+ m_VulkanFunctions.vkInvalidateMappedMemoryRanges = &vkInvalidateMappedMemoryRanges;
+ m_VulkanFunctions.vkBindBufferMemory = &vkBindBufferMemory;
+ m_VulkanFunctions.vkBindImageMemory = &vkBindImageMemory;
+ m_VulkanFunctions.vkGetBufferMemoryRequirements = &vkGetBufferMemoryRequirements;
+ m_VulkanFunctions.vkGetImageMemoryRequirements = &vkGetImageMemoryRequirements;
+ m_VulkanFunctions.vkCreateBuffer = &vkCreateBuffer;
+ m_VulkanFunctions.vkDestroyBuffer = &vkDestroyBuffer;
+ m_VulkanFunctions.vkCreateImage = &vkCreateImage;
+ m_VulkanFunctions.vkDestroyImage = &vkDestroyImage;
+ m_VulkanFunctions.vkCmdCopyBuffer = &vkCmdCopyBuffer;
+#if VMA_DEDICATED_ALLOCATION
+ if(m_UseKhrDedicatedAllocation)
+ {
+ m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR =
+ (PFN_vkGetBufferMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetBufferMemoryRequirements2KHR");
+ m_VulkanFunctions.vkGetImageMemoryRequirements2KHR =
+ (PFN_vkGetImageMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetImageMemoryRequirements2KHR");
+ }
+#endif // #if VMA_DEDICATED_ALLOCATION
+#endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
+
+#define VMA_COPY_IF_NOT_NULL(funcName) \
+ if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
+
+ if(pVulkanFunctions != VMA_NULL)
+ {
+ VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
+ VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
+ VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
+ VMA_COPY_IF_NOT_NULL(vkFreeMemory);
+ VMA_COPY_IF_NOT_NULL(vkMapMemory);
+ VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
+ VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
+ VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
+ VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
+ VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
+ VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
+ VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
+ VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
+ VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
+ VMA_COPY_IF_NOT_NULL(vkCreateImage);
+ VMA_COPY_IF_NOT_NULL(vkDestroyImage);
+ VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
+#if VMA_DEDICATED_ALLOCATION
+ VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
+ VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
+#endif
+ }
+
+#undef VMA_COPY_IF_NOT_NULL
+
+ // If these asserts are hit, you must either #define VMA_STATIC_VULKAN_FUNCTIONS 1
+ // or pass valid pointers as VmaAllocatorCreateInfo::pVulkanFunctions.
+ VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
+#if VMA_DEDICATED_ALLOCATION
+ if(m_UseKhrDedicatedAllocation)
+ {
+ VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
+ }
+#endif
+}
+
+VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
+{
+ const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
+ const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
+ const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
+ return isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize;
+}
+
+VkResult VmaAllocator_T::AllocateMemoryOfType(
+ VkDeviceSize size,
+ VkDeviceSize alignment,
+ bool dedicatedAllocation,
+ VkBuffer dedicatedBuffer,
+ VkImage dedicatedImage,
+ const VmaAllocationCreateInfo& createInfo,
+ uint32_t memTypeIndex,
+ VmaSuballocationType suballocType,
+ size_t allocationCount,
+ VmaAllocation* pAllocations)
+{
+ VMA_ASSERT(pAllocations != VMA_NULL);
+ VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, vkMemReq.size);
+
+ VmaAllocationCreateInfo finalCreateInfo = createInfo;
+
+ // If memory type is not HOST_VISIBLE, disable MAPPED.
+ if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
+ (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
+ {
+ finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
+ }
+
+ VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
+ VMA_ASSERT(blockVector);
+
+ const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
+ bool preferDedicatedMemory =
+ VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
+ dedicatedAllocation ||
+ // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
+ size > preferredBlockSize / 2;
+
+ if(preferDedicatedMemory &&
+ (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
+ finalCreateInfo.pool == VK_NULL_HANDLE)
+ {
+ finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
+ }
+
+ if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
+ {
+ if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
+ {
+ return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+ }
+ else
+ {
+ return AllocateDedicatedMemory(
+ size,
+ suballocType,
+ memTypeIndex,
+ (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
+ (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
+ finalCreateInfo.pUserData,
+ dedicatedBuffer,
+ dedicatedImage,
+ allocationCount,
+ pAllocations);
+ }
+ }
+ else
+ {
+ VkResult res = blockVector->Allocate(
+ VK_NULL_HANDLE, // hCurrentPool
+ m_CurrentFrameIndex.load(),
+ size,
+ alignment,
+ finalCreateInfo,
+ suballocType,
+ allocationCount,
+ pAllocations);
+ if(res == VK_SUCCESS)
+ {
+ return res;
+ }
+
+ // 5. Try dedicated memory.
+ if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
+ {
+ return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+ }
+ else
+ {
+ res = AllocateDedicatedMemory(
+ size,
+ suballocType,
+ memTypeIndex,
+ (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
+ (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
+ finalCreateInfo.pUserData,
+ dedicatedBuffer,
+ dedicatedImage,
+ allocationCount,
+ pAllocations);
+ if(res == VK_SUCCESS)
+ {
+ // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
+ VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
+ return VK_SUCCESS;
+ }
+ else
+ {
+ // Everything failed: Return error code.
+ VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
+ return res;
+ }
+ }
+ }
+}
+
+VkResult VmaAllocator_T::AllocateDedicatedMemory(
+ VkDeviceSize size,
+ VmaSuballocationType suballocType,
+ uint32_t memTypeIndex,
+ bool map,
+ bool isUserDataString,
+ void* pUserData,
+ VkBuffer /*dedicatedBuffer*/,
+ VkImage /*dedicatedImage*/,
+ size_t allocationCount,
+ VmaAllocation* pAllocations)
+{
+ VMA_ASSERT(allocationCount > 0 && pAllocations);
+
+ VkMemoryAllocateInfo allocInfo = {};
+ allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ allocInfo.memoryTypeIndex = memTypeIndex;
+ allocInfo.allocationSize = size;
+
+#if VMA_DEDICATED_ALLOCATION
+ VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = {};
+ dedicatedAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR;
+ if(m_UseKhrDedicatedAllocation)
+ {
+ if(dedicatedBuffer != VK_NULL_HANDLE)
+ {
+ VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
+ dedicatedAllocInfo.buffer = dedicatedBuffer;
+ allocInfo.pNext = &dedicatedAllocInfo;
+ }
+ else if(dedicatedImage != VK_NULL_HANDLE)
+ {
+ dedicatedAllocInfo.image = dedicatedImage;
+ allocInfo.pNext = &dedicatedAllocInfo;
+ }
+ }
+#endif // #if VMA_DEDICATED_ALLOCATION
+
+ size_t allocIndex;
+ VkResult res = VK_SUCCESS;
+ for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
+ {
+ res = AllocateDedicatedMemoryPage(
+ size,
+ suballocType,
+ memTypeIndex,
+ allocInfo,
+ map,
+ isUserDataString,
+ pUserData,
+ pAllocations + allocIndex);
+ if(res != VK_SUCCESS)
+ {
+ break;
+ }
+ }
+
+ if(res == VK_SUCCESS)
+ {
+ // Register them in m_pDedicatedAllocations.
+ {
+ VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
+ AllocationVectorType* pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
+ VMA_ASSERT(pDedicatedAllocations);
+ for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
+ {
+ VmaVectorInsertSorted<VmaPointerLess>(*pDedicatedAllocations, pAllocations[allocIndex]);
+ }
+ }
+
+ VMA_DEBUG_LOG(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
+ }
+ else
+ {
+ // Free all already created allocations.
+ while(allocIndex--)
+ {
+ VmaAllocation currAlloc = pAllocations[allocIndex];
+ VkDeviceMemory hMemory = currAlloc->GetMemory();
+
+ /*
+ There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
+ before vkFreeMemory.
+
+ if(currAlloc->GetMappedData() != VMA_NULL)
+ {
+ (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
+ }
+ */
+
+ FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
+
+ currAlloc->SetUserData(this, VMA_NULL);
+ vma_delete(this, currAlloc);
+ }
+
+ memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
+ }
+
+ return res;
+}
+
+VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
+ VkDeviceSize size,
+ VmaSuballocationType suballocType,
+ uint32_t memTypeIndex,
+ const VkMemoryAllocateInfo& allocInfo,
+ bool map,
+ bool isUserDataString,
+ void* pUserData,
+ VmaAllocation* pAllocation)
+{
+ VkDeviceMemory hMemory = VK_NULL_HANDLE;
+ VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
+ if(res < 0)
+ {
+ VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
+ return res;
+ }
+
+ void* pMappedData = VMA_NULL;
+ if(map)
+ {
+ res = (*m_VulkanFunctions.vkMapMemory)(
+ m_hDevice,
+ hMemory,
+ 0,
+ VK_WHOLE_SIZE,
+ 0,
+ &pMappedData);
+ if(res < 0)
+ {
+ VMA_DEBUG_LOG(" vkMapMemory FAILED");
+ FreeVulkanMemory(memTypeIndex, size, hMemory);
+ return res;
+ }
+ }
+
+ *pAllocation = vma_new(this, VmaAllocation_T)(m_CurrentFrameIndex.load(), isUserDataString);
+ (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
+ (*pAllocation)->SetUserData(this, pUserData);
+ if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
+ {
+ FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
+ }
+
+ return VK_SUCCESS;
+}
+
+void VmaAllocator_T::GetBufferMemoryRequirements(
+ VkBuffer hBuffer,
+ VkMemoryRequirements& memReq,
+ bool& requiresDedicatedAllocation,
+ bool& prefersDedicatedAllocation) const
+{
+#if VMA_DEDICATED_ALLOCATION
+ if(m_UseKhrDedicatedAllocation)
+ {
+ VkBufferMemoryRequirementsInfo2KHR memReqInfo = {};
+ memReqInfo.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR;
+ memReqInfo.buffer = hBuffer;
+
+ VkMemoryDedicatedRequirementsKHR memDedicatedReq = {};
+ memDedicatedReq.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR;
+
+ VkMemoryRequirements2KHR memReq2 = {};
+ memReq2.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR;
+ memReq2.pNext = &memDedicatedReq;
+
+ (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
+
+ memReq = memReq2.memoryRequirements;
+ requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
+ prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
+ }
+ else
+#endif // #if VMA_DEDICATED_ALLOCATION
+ {
+ (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
+ requiresDedicatedAllocation = false;
+ prefersDedicatedAllocation = false;
+ }
+}
+
+void VmaAllocator_T::GetImageMemoryRequirements(
+ VkImage hImage,
+ VkMemoryRequirements& memReq,
+ bool& requiresDedicatedAllocation,
+ bool& prefersDedicatedAllocation) const
+{
+#if VMA_DEDICATED_ALLOCATION
+ if(m_UseKhrDedicatedAllocation)
+ {
+ VkImageMemoryRequirementsInfo2KHR memReqInfo = {};
+ memReqInfo.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR;
+ memReqInfo.image = hImage;
+
+ VkMemoryDedicatedRequirementsKHR memDedicatedReq = {};
+ memDedicatedReq.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR;
+
+ VkMemoryRequirements2KHR memReq2 = {};
+ memReq2.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR;
+ memReq2.pNext = &memDedicatedReq;
+
+ (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
+
+ memReq = memReq2.memoryRequirements;
+ requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
+ prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
+ }
+ else
+#endif // #if VMA_DEDICATED_ALLOCATION
+ {
+ (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
+ requiresDedicatedAllocation = false;
+ prefersDedicatedAllocation = false;
+ }
+}
+
+VkResult VmaAllocator_T::AllocateMemory(
+ const VkMemoryRequirements& vkMemReq,
+ bool requiresDedicatedAllocation,
+ bool prefersDedicatedAllocation,
+ VkBuffer dedicatedBuffer,
+ VkImage dedicatedImage,
+ const VmaAllocationCreateInfo& createInfo,
+ VmaSuballocationType suballocType,
+ size_t allocationCount,
+ VmaAllocation* pAllocations)
+{
+ memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
+
+ VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
+
+ if(vkMemReq.size == 0)
+ {
+ return VK_ERROR_VALIDATION_FAILED_EXT;
+ }
+ if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
+ (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
+ {
+ VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
+ return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+ }
+ if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
+ (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0)
+ {
+ VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
+ return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+ }
+ if(requiresDedicatedAllocation)
+ {
+ if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
+ {
+ VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
+ return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+ }
+ if(createInfo.pool != VK_NULL_HANDLE)
+ {
+ VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
+ return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+ }
+ }
+ if((createInfo.pool != VK_NULL_HANDLE) &&
+ ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
+ {
+ VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
+ return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+ }
+
+ if(createInfo.pool != VK_NULL_HANDLE)
+ {
+ const VkDeviceSize alignmentForPool = VMA_MAX(
+ vkMemReq.alignment,
+ GetMemoryTypeMinAlignment(createInfo.pool->m_BlockVector.GetMemoryTypeIndex()));
+ return createInfo.pool->m_BlockVector.Allocate(
+ createInfo.pool,
+ m_CurrentFrameIndex.load(),
+ vkMemReq.size,
+ alignmentForPool,
+ createInfo,
+ suballocType,
+ allocationCount,
+ pAllocations);
+ }
+ else
+ {
+ // Bit mask of memory Vulkan types acceptable for this allocation.
+ uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
+ uint32_t memTypeIndex = UINT32_MAX;
+ VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
+ if(res == VK_SUCCESS)
+ {
+ VkDeviceSize alignmentForMemType = VMA_MAX(
+ vkMemReq.alignment,
+ GetMemoryTypeMinAlignment(memTypeIndex));
+
+ res = AllocateMemoryOfType(
+ vkMemReq.size,
+ alignmentForMemType,
+ requiresDedicatedAllocation || prefersDedicatedAllocation,
+ dedicatedBuffer,
+ dedicatedImage,
+ createInfo,
+ memTypeIndex,
+ suballocType,
+ allocationCount,
+ pAllocations);
+ // Succeeded on first try.
+ if(res == VK_SUCCESS)
+ {
+ return res;
+ }
+ // Allocation from this memory type failed. Try other compatible memory types.
+ else
+ {
+ for(;;)
+ {
+ // Remove old memTypeIndex from list of possibilities.
+ memoryTypeBits &= ~(1u << memTypeIndex);
+ // Find alternative memTypeIndex.
+ res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
+ if(res == VK_SUCCESS)
+ {
+ alignmentForMemType = VMA_MAX(
+ vkMemReq.alignment,
+ GetMemoryTypeMinAlignment(memTypeIndex));
+
+ res = AllocateMemoryOfType(
+ vkMemReq.size,
+ alignmentForMemType,
+ requiresDedicatedAllocation || prefersDedicatedAllocation,
+ dedicatedBuffer,
+ dedicatedImage,
+ createInfo,
+ memTypeIndex,
+ suballocType,
+ allocationCount,
+ pAllocations);
+ // Allocation from this alternative memory type succeeded.
+ if(res == VK_SUCCESS)
+ {
+ return res;
+ }
+ // else: Allocation from this memory type failed. Try next one - next loop iteration.
+ }
+ // No other matching memory type index could be found.
+ else
+ {
+ // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
+ return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+ }
+ }
+ }
+ }
+ // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
+ else
+ return res;
+ }
+}
+
+void VmaAllocator_T::FreeMemory(
+ size_t allocationCount,
+ const VmaAllocation* pAllocations)
+{
+ VMA_ASSERT(pAllocations);
+
+ for(size_t allocIndex = allocationCount; allocIndex--; )
+ {
+ VmaAllocation allocation = pAllocations[allocIndex];
+
+ if(allocation != VK_NULL_HANDLE)
+ {
+ if(TouchAllocation(allocation))
+ {
+ if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
+ {
+ FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
+ }
+
+ switch(allocation->GetType())
+ {
+ case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
+ {
+ VmaBlockVector* pBlockVector = VMA_NULL;
+ VmaPool hPool = allocation->GetPool();
+ if(hPool != VK_NULL_HANDLE)
+ {
+ pBlockVector = &hPool->m_BlockVector;
+ }
+ else
+ {
+ const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
+ pBlockVector = m_pBlockVectors[memTypeIndex];
+ }
+ pBlockVector->Free(allocation);
+ }
+ break;
+ case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
+ FreeDedicatedMemory(allocation);
+ break;
+ default:
+ VMA_ASSERT(0);
+ }
+ }
+
+ allocation->SetUserData(this, VMA_NULL);
+ vma_delete(this, allocation);
+ }
+ }
+}
+
+VkResult VmaAllocator_T::ResizeAllocation(
+ const VmaAllocation alloc,
+ VkDeviceSize newSize)
+{
+ if(newSize == 0 || alloc->GetLastUseFrameIndex() == VMA_FRAME_INDEX_LOST)
+ {
+ return VK_ERROR_VALIDATION_FAILED_EXT;
+ }
+ if(newSize == alloc->GetSize())
+ {
+ return VK_SUCCESS;
+ }
+
+ switch(alloc->GetType())
+ {
+ case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
+ return VK_ERROR_FEATURE_NOT_PRESENT;
+ case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
+ if(alloc->GetBlock()->m_pMetadata->ResizeAllocation(alloc, newSize))
+ {
+ alloc->ChangeSize(newSize);
+ VMA_HEAVY_ASSERT(alloc->GetBlock()->m_pMetadata->Validate());
+ return VK_SUCCESS;
+ }
+ else
+ {
+ return VkResult(-1000069000); // VK_ERROR_OUT_OF_POOL_MEMORY
+ }
+ default:
+ VMA_ASSERT(0);
+ return VK_ERROR_VALIDATION_FAILED_EXT;
+ }
+}
+
+void VmaAllocator_T::CalculateStats(VmaStats* pStats)
+{
+ // Initialize.
+ InitStatInfo(pStats->total);
+ for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
+ InitStatInfo(pStats->memoryType[i]);
+ for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
+ InitStatInfo(pStats->memoryHeap[i]);
+
+ // Process default pools.
+ for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
+ {
+ VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
+ VMA_ASSERT(pBlockVector);
+ pBlockVector->AddStats(pStats);
+ }
+
+ // Process custom pools.
+ {
+ VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
+ for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
+ {
+ m_Pools[poolIndex]->m_BlockVector.AddStats(pStats);
+ }
+ }
+
+ // Process dedicated allocations.
+ for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
+ {
+ const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
+ VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
+ AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
+ VMA_ASSERT(pDedicatedAllocVector);
+ for(size_t allocIndex = 0, allocCount = pDedicatedAllocVector->size(); allocIndex < allocCount; ++allocIndex)
+ {
+ VmaStatInfo allocationStatInfo;
+ (*pDedicatedAllocVector)[allocIndex]->DedicatedAllocCalcStatsInfo(allocationStatInfo);
+ VmaAddStatInfo(pStats->total, allocationStatInfo);
+ VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
+ VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
+ }
+ }
+
+ // Postprocess.
+ VmaPostprocessCalcStatInfo(pStats->total);
+ for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
+ VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
+ for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
+ VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
+}
+
+static const uint32_t VMA_VENDOR_ID_AMD = 4098;
+
+VkResult VmaAllocator_T::DefragmentationBegin(
+ const VmaDefragmentationInfo2& info,
+ VmaDefragmentationStats* pStats,
+ VmaDefragmentationContext* pContext)
+{
+ if(info.pAllocationsChanged != VMA_NULL)
+ {
+ memset(info.pAllocationsChanged, 0, info.allocationCount * sizeof(VkBool32));
+ }
+
+ *pContext = vma_new(this, VmaDefragmentationContext_T)(
+ this, m_CurrentFrameIndex.load(), info.flags, pStats);
+
+ (*pContext)->AddPools(info.poolCount, info.pPools);
+ (*pContext)->AddAllocations(
+ info.allocationCount, info.pAllocations, info.pAllocationsChanged);
+
+ VkResult res = (*pContext)->Defragment(
+ info.maxCpuBytesToMove, info.maxCpuAllocationsToMove,
+ info.maxGpuBytesToMove, info.maxGpuAllocationsToMove,
+ info.commandBuffer, pStats);
+
+ if(res != VK_NOT_READY)
+ {
+ vma_delete(this, *pContext);
+ *pContext = VMA_NULL;
+ }
+
+ return res;
+}
+
+VkResult VmaAllocator_T::DefragmentationEnd(
+ VmaDefragmentationContext context)
+{
+ vma_delete(this, context);
+ return VK_SUCCESS;
+}
+
+void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
+{
+ if(hAllocation->CanBecomeLost())
+ {
+ /*
+ Warning: This is a carefully designed algorithm.
+ Do not modify unless you really know what you're doing :)
+ */
+ const uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
+ uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
+ for(;;)
+ {
+ if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
+ {
+ pAllocationInfo->memoryType = UINT32_MAX;
+ pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
+ pAllocationInfo->offset = 0;
+ pAllocationInfo->size = hAllocation->GetSize();
+ pAllocationInfo->pMappedData = VMA_NULL;
+ pAllocationInfo->pUserData = hAllocation->GetUserData();
+ return;
+ }
+ else if(localLastUseFrameIndex == localCurrFrameIndex)
+ {
+ pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
+ pAllocationInfo->deviceMemory = hAllocation->GetMemory();
+ pAllocationInfo->offset = hAllocation->GetOffset();
+ pAllocationInfo->size = hAllocation->GetSize();
+ pAllocationInfo->pMappedData = VMA_NULL;
+ pAllocationInfo->pUserData = hAllocation->GetUserData();
+ return;
+ }
+ else // Last use time earlier than current time.
+ {
+ if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
+ {
+ localLastUseFrameIndex = localCurrFrameIndex;
+ }
+ }
+ }
+ }
+ else
+ {
+#if VMA_STATS_STRING_ENABLED
+ uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
+ uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
+ for(;;)
+ {
+ VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
+ if(localLastUseFrameIndex == localCurrFrameIndex)
+ {
+ break;
+ }
+ else // Last use time earlier than current time.
+ {
+ if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
+ {
+ localLastUseFrameIndex = localCurrFrameIndex;
+ }
+ }
+ }
+#endif
+
+ pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
+ pAllocationInfo->deviceMemory = hAllocation->GetMemory();
+ pAllocationInfo->offset = hAllocation->GetOffset();
+ pAllocationInfo->size = hAllocation->GetSize();
+ pAllocationInfo->pMappedData = hAllocation->GetMappedData();
+ pAllocationInfo->pUserData = hAllocation->GetUserData();
+ }
+}
+
+bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation)
+{
+ // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo.
+ if(hAllocation->CanBecomeLost())
+ {
+ uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
+ uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
+ for(;;)
+ {
+ if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
+ {
+ return false;
+ }
+ else if(localLastUseFrameIndex == localCurrFrameIndex)
+ {
+ return true;
+ }
+ else // Last use time earlier than current time.
+ {
+ if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
+ {
+ localLastUseFrameIndex = localCurrFrameIndex;
+ }
+ }
+ }
+ }
+ else
+ {
+#if VMA_STATS_STRING_ENABLED
+ uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
+ uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
+ for(;;)
+ {
+ VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
+ if(localLastUseFrameIndex == localCurrFrameIndex)
+ {
+ break;
+ }
+ else // Last use time earlier than current time.
+ {
+ if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
+ {
+ localLastUseFrameIndex = localCurrFrameIndex;
+ }
+ }
+ }
+#endif
+
+ return true;
+ }
+}
+
+VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
+{
+ VMA_DEBUG_LOG(" CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
+
+ VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
+
+ if(newCreateInfo.maxBlockCount == 0)
+ {
+ newCreateInfo.maxBlockCount = SIZE_MAX;
+ }
+ if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
+ {
+ return VK_ERROR_INITIALIZATION_FAILED;
+ }
+
+ const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
+
+ *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
+
+ VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
+ if(res != VK_SUCCESS)
+ {
+ vma_delete(this, *pPool);
+ *pPool = VMA_NULL;
+ return res;
+ }
+
+ // Add to m_Pools.
+ {
+ VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
+ (*pPool)->SetId(m_NextPoolId++);
+ VmaVectorInsertSorted<VmaPointerLess>(m_Pools, *pPool);
+ }
+
+ return VK_SUCCESS;
+}
+
+void VmaAllocator_T::DestroyPool(VmaPool pool)
+{
+ // Remove from m_Pools.
+ {
+ VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
+ bool success = VmaVectorRemoveSorted<VmaPointerLess>(m_Pools, pool);
+ (void) success;
+ VMA_ASSERT(success && "Pool not found in Allocator.");
+ }
+
+ vma_delete(this, pool);
+}
+
+void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
+{
+ pool->m_BlockVector.GetPoolStats(pPoolStats);
+}
+
+void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
+{
+ m_CurrentFrameIndex.store(frameIndex);
+}
+
+void VmaAllocator_T::MakePoolAllocationsLost(
+ VmaPool hPool,
+ size_t* pLostAllocationCount)
+{
+ hPool->m_BlockVector.MakePoolAllocationsLost(
+ m_CurrentFrameIndex.load(),
+ pLostAllocationCount);
+}
+
+VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
+{
+ return hPool->m_BlockVector.CheckCorruption();
+}
+
+VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
+{
+ VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
+
+ // Process default pools.
+ for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
+ {
+ if(((1u << memTypeIndex) & memoryTypeBits) != 0)
+ {
+ VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
+ VMA_ASSERT(pBlockVector);
+ VkResult localRes = pBlockVector->CheckCorruption();
+ switch(localRes)
+ {
+ case VK_ERROR_FEATURE_NOT_PRESENT:
+ break;
+ case VK_SUCCESS:
+ finalRes = VK_SUCCESS;
+ break;
+ default:
+ return localRes;
+ }
+ }
+ }
+
+ // Process custom pools.
+ {
+ VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
+ for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
+ {
+ if(((1u << m_Pools[poolIndex]->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
+ {
+ VkResult localRes = m_Pools[poolIndex]->m_BlockVector.CheckCorruption();
+ switch(localRes)
+ {
+ case VK_ERROR_FEATURE_NOT_PRESENT:
+ break;
+ case VK_SUCCESS:
+ finalRes = VK_SUCCESS;
+ break;
+ default:
+ return localRes;
+ }
+ }
+ }
+ }
+
+ return finalRes;
+}
+
+void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
+{
+ *pAllocation = vma_new(this, VmaAllocation_T)(VMA_FRAME_INDEX_LOST, false);
+ (*pAllocation)->InitLost();
+}
+
+VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
+{
+ const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
+
+ VkResult res;
+ if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE)
+ {
+ VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex);
+ if(m_HeapSizeLimit[heapIndex] >= pAllocateInfo->allocationSize)
+ {
+ res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
+ if(res == VK_SUCCESS)
+ {
+ m_HeapSizeLimit[heapIndex] -= pAllocateInfo->allocationSize;
+ }
+ }
+ else
+ {
+ res = VK_ERROR_OUT_OF_DEVICE_MEMORY;
+ }
+ }
+ else
+ {
+ res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
+ }
+
+ if(res == VK_SUCCESS && m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
+ {
+ (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize);
+ }
+
+ return res;
+}
+
+void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
+{
+ if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
+ {
+ (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size);
+ }
+
+ (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
+
+ const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memoryType);
+ if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE)
+ {
+ VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex);
+ m_HeapSizeLimit[heapIndex] += size;
+ }
+}
+
+VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
+{
+ if(hAllocation->CanBecomeLost())
+ {
+ return VK_ERROR_MEMORY_MAP_FAILED;
+ }
+
+ switch(hAllocation->GetType())
+ {
+ case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
+ {
+ VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
+ char *pBytes = VMA_NULL;
+ VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
+ if(res == VK_SUCCESS)
+ {
+ *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
+ hAllocation->BlockAllocMap();
+ }
+ return res;
+ }
+ case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
+ return hAllocation->DedicatedAllocMap(this, ppData);
+ default:
+ VMA_ASSERT(0);
+ return VK_ERROR_MEMORY_MAP_FAILED;
+ }
+}
+
+void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
+{
+ switch(hAllocation->GetType())
+ {
+ case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
+ {
+ VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
+ hAllocation->BlockAllocUnmap();
+ pBlock->Unmap(this, 1);
+ }
+ break;
+ case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
+ hAllocation->DedicatedAllocUnmap(this);
+ break;
+ default:
+ VMA_ASSERT(0);
+ }
+}
+
+VkResult VmaAllocator_T::BindBufferMemory(VmaAllocation hAllocation, VkBuffer hBuffer)
+{
+ VkResult res = VK_SUCCESS;
+ switch(hAllocation->GetType())
+ {
+ case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
+ res = GetVulkanFunctions().vkBindBufferMemory(
+ m_hDevice,
+ hBuffer,
+ hAllocation->GetMemory(),
+ 0); //memoryOffset
+ break;
+ case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
+ {
+ VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
+ VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?");
+ res = pBlock->BindBufferMemory(this, hAllocation, hBuffer);
+ break;
+ }
+ default:
+ VMA_ASSERT(0);
+ }
+ return res;
+}
+
+VkResult VmaAllocator_T::BindImageMemory(VmaAllocation hAllocation, VkImage hImage)
+{
+ VkResult res = VK_SUCCESS;
+ switch(hAllocation->GetType())
+ {
+ case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
+ res = GetVulkanFunctions().vkBindImageMemory(
+ m_hDevice,
+ hImage,
+ hAllocation->GetMemory(),
+ 0); //memoryOffset
+ break;
+ case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
+ {
+ VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
+ VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?");
+ res = pBlock->BindImageMemory(this, hAllocation, hImage);
+ break;
+ }
+ default:
+ VMA_ASSERT(0);
+ }
+ return res;
+}
+
+void VmaAllocator_T::FlushOrInvalidateAllocation(
+ VmaAllocation hAllocation,
+ VkDeviceSize offset, VkDeviceSize size,
+ VMA_CACHE_OPERATION op)
+{
+ const uint32_t memTypeIndex = hAllocation->GetMemoryTypeIndex();
+ if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
+ {
+ const VkDeviceSize allocationSize = hAllocation->GetSize();
+ VMA_ASSERT(offset <= allocationSize);
+
+ const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
+
+ VkMappedMemoryRange memRange = {};
+ memRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
+ memRange.memory = hAllocation->GetMemory();
+
+ switch(hAllocation->GetType())
+ {
+ case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
+ memRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
+ if(size == VK_WHOLE_SIZE)
+ {
+ memRange.size = allocationSize - memRange.offset;
+ }
+ else
+ {
+ VMA_ASSERT(offset + size <= allocationSize);
+ memRange.size = VMA_MIN(
+ VmaAlignUp(size + (offset - memRange.offset), nonCoherentAtomSize),
+ allocationSize - memRange.offset);
+ }
+ break;
+
+ case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
+ {
+ // 1. Still within this allocation.
+ memRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
+ if(size == VK_WHOLE_SIZE)
+ {
+ size = allocationSize - offset;
+ }
+ else
+ {
+ VMA_ASSERT(offset + size <= allocationSize);
+ }
+ memRange.size = VmaAlignUp(size + (offset - memRange.offset), nonCoherentAtomSize);
+
+ // 2. Adjust to whole block.
+ const VkDeviceSize allocationOffset = hAllocation->GetOffset();
+ VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
+ const VkDeviceSize blockSize = hAllocation->GetBlock()->m_pMetadata->GetSize();
+ memRange.offset += allocationOffset;
+ memRange.size = VMA_MIN(memRange.size, blockSize - memRange.offset);
+
+ break;
+ }
+
+ default:
+ VMA_ASSERT(0);
+ }
+
+ switch(op)
+ {
+ case VMA_CACHE_FLUSH:
+ (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
+ break;
+ case VMA_CACHE_INVALIDATE:
+ (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
+ break;
+ default:
+ VMA_ASSERT(0);
+ }
+ }
+ // else: Just ignore this call.
+}
+
+void VmaAllocator_T::FreeDedicatedMemory(VmaAllocation allocation)
+{
+ VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
+
+ const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
+ {
+ VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
+ AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
+ VMA_ASSERT(pDedicatedAllocations);
+ bool success = VmaVectorRemoveSorted<VmaPointerLess>(*pDedicatedAllocations, allocation);
+ (void) success;
+ VMA_ASSERT(success);
+ }
+
+ VkDeviceMemory hMemory = allocation->GetMemory();
+
+ /*
+ There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
+ before vkFreeMemory.
+
+ if(allocation->GetMappedData() != VMA_NULL)
+ {
+ (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
+ }
+ */
+
+ FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
+
+ VMA_DEBUG_LOG(" Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
+}
+
+void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
+{
+ if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
+ !hAllocation->CanBecomeLost() &&
+ (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
+ {
+ void* pData = VMA_NULL;
+ VkResult res = Map(hAllocation, &pData);
+ if(res == VK_SUCCESS)
+ {
+ memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
+ FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
+ Unmap(hAllocation);
+ }
+ else
+ {
+ VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
+ }
+ }
+}
+
+#if VMA_STATS_STRING_ENABLED
+
+void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
+{
+ bool dedicatedAllocationsStarted = false;
+ for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
+ {
+ VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
+ AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
+ VMA_ASSERT(pDedicatedAllocVector);
+ if(pDedicatedAllocVector->empty() == false)
+ {
+ if(dedicatedAllocationsStarted == false)
+ {
+ dedicatedAllocationsStarted = true;
+ json.WriteString("DedicatedAllocations");
+ json.BeginObject();
+ }
+
+ json.BeginString("Type ");
+ json.ContinueString(memTypeIndex);
+ json.EndString();
+
+ json.BeginArray();
+
+ for(size_t i = 0; i < pDedicatedAllocVector->size(); ++i)
+ {
+ json.BeginObject(true);
+ const VmaAllocation hAlloc = (*pDedicatedAllocVector)[i];
+ hAlloc->PrintParameters(json);
+ json.EndObject();
+ }
+
+ json.EndArray();
+ }
+ }
+ if(dedicatedAllocationsStarted)
+ {
+ json.EndObject();
+ }
+
+ {
+ bool allocationsStarted = false;
+ for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
+ {
+ if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
+ {
+ if(allocationsStarted == false)
+ {
+ allocationsStarted = true;
+ json.WriteString("DefaultPools");
+ json.BeginObject();
+ }
+
+ json.BeginString("Type ");
+ json.ContinueString(memTypeIndex);
+ json.EndString();
+
+ m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
+ }
+ }
+ if(allocationsStarted)
+ {
+ json.EndObject();
+ }
+ }
+
+ // Custom pools
+ {
+ VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
+ const size_t poolCount = m_Pools.size();
+ if(poolCount > 0)
+ {
+ json.WriteString("Pools");
+ json.BeginObject();
+ for(size_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
+ {
+ json.BeginString();
+ json.ContinueString(m_Pools[poolIndex]->GetId());
+ json.EndString();
+
+ m_Pools[poolIndex]->m_BlockVector.PrintDetailedMap(json);
+ }
+ json.EndObject();
+ }
+ }
+}
+
+#endif // #if VMA_STATS_STRING_ENABLED
+
+////////////////////////////////////////////////////////////////////////////////
+// Public interface
+
+VkResult vmaCreateAllocator(
+ const VmaAllocatorCreateInfo* pCreateInfo,
+ VmaAllocator* pAllocator)
+{
+ VMA_ASSERT(pCreateInfo && pAllocator);
+ VMA_DEBUG_LOG("vmaCreateAllocator");
+ *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
+ return (*pAllocator)->Init(pCreateInfo);
+}
+
+void vmaDestroyAllocator(
+ VmaAllocator allocator)
+{
+ if(allocator != VK_NULL_HANDLE)
+ {
+ VMA_DEBUG_LOG("vmaDestroyAllocator");
+ VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
+ vma_delete(&allocationCallbacks, allocator);
+ }
+}
+
+void vmaGetPhysicalDeviceProperties(
+ VmaAllocator allocator,
+ const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
+{
+ VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
+ *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
+}
+
+void vmaGetMemoryProperties(
+ VmaAllocator allocator,
+ const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
+{
+ VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
+ *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
+}
+
+void vmaGetMemoryTypeProperties(
+ VmaAllocator allocator,
+ uint32_t memoryTypeIndex,
+ VkMemoryPropertyFlags* pFlags)
+{
+ VMA_ASSERT(allocator && pFlags);
+ VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
+ *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
+}
+
+void vmaSetCurrentFrameIndex(
+ VmaAllocator allocator,
+ uint32_t frameIndex)
+{
+ VMA_ASSERT(allocator);
+ VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ allocator->SetCurrentFrameIndex(frameIndex);
+}
+
+void vmaCalculateStats(
+ VmaAllocator allocator,
+ VmaStats* pStats)
+{
+ VMA_ASSERT(allocator && pStats);
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+ allocator->CalculateStats(pStats);
+}
+
+#if VMA_STATS_STRING_ENABLED
+
+void vmaBuildStatsString(
+ VmaAllocator allocator,
+ char** ppStatsString,
+ VkBool32 detailedMap)
+{
+ VMA_ASSERT(allocator && ppStatsString);
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ VmaStringBuilder sb(allocator);
+ {
+ VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
+ json.BeginObject();
+
+ VmaStats stats;
+ allocator->CalculateStats(&stats);
+
+ json.WriteString("Total");
+ VmaPrintStatInfo(json, stats.total);
+
+ for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
+ {
+ json.BeginString("Heap ");
+ json.ContinueString(heapIndex);
+ json.EndString();
+ json.BeginObject();
+
+ json.WriteString("Size");
+ json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
+
+ json.WriteString("Flags");
+ json.BeginArray(true);
+ if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
+ {
+ json.WriteString("DEVICE_LOCAL");
+ }
+ json.EndArray();
+
+ if(stats.memoryHeap[heapIndex].blockCount > 0)
+ {
+ json.WriteString("Stats");
+ VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]);
+ }
+
+ for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
+ {
+ if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
+ {
+ json.BeginString("Type ");
+ json.ContinueString(typeIndex);
+ json.EndString();
+
+ json.BeginObject();
+
+ json.WriteString("Flags");
+ json.BeginArray(true);
+ VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
+ if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
+ {
+ json.WriteString("DEVICE_LOCAL");
+ }
+ if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
+ {
+ json.WriteString("HOST_VISIBLE");
+ }
+ if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
+ {
+ json.WriteString("HOST_COHERENT");
+ }
+ if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
+ {
+ json.WriteString("HOST_CACHED");
+ }
+ if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
+ {
+ json.WriteString("LAZILY_ALLOCATED");
+ }
+ json.EndArray();
+
+ if(stats.memoryType[typeIndex].blockCount > 0)
+ {
+ json.WriteString("Stats");
+ VmaPrintStatInfo(json, stats.memoryType[typeIndex]);
+ }
+
+ json.EndObject();
+ }
+ }
+
+ json.EndObject();
+ }
+ if(detailedMap == VK_TRUE)
+ {
+ allocator->PrintDetailedMap(json);
+ }
+
+ json.EndObject();
+ }
+
+ const size_t len = sb.GetLength();
+ char* const pChars = vma_new_array(allocator, char, len + 1);
+ if(len > 0)
+ {
+ memcpy(pChars, sb.GetData(), len);
+ }
+ pChars[len] = '\0';
+ *ppStatsString = pChars;
+}
+
+void vmaFreeStatsString(
+ VmaAllocator allocator,
+ char* pStatsString)
+{
+ if(pStatsString != VMA_NULL)
+ {
+ VMA_ASSERT(allocator);
+ size_t len = strlen(pStatsString);
+ vma_delete_array(allocator, pStatsString, len + 1);
+ }
+}
+
+#endif // #if VMA_STATS_STRING_ENABLED
+
+/*
+This function is not protected by any mutex because it just reads immutable data.
+*/
+VkResult vmaFindMemoryTypeIndex(
+ VmaAllocator allocator,
+ uint32_t memoryTypeBits,
+ const VmaAllocationCreateInfo* pAllocationCreateInfo,
+ uint32_t* pMemoryTypeIndex)
+{
+ VMA_ASSERT(allocator != VK_NULL_HANDLE);
+ VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
+ VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
+
+ if(pAllocationCreateInfo->memoryTypeBits != 0)
+ {
+ memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
+ }
+
+ uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
+ uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
+
+ const bool mapped = (pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
+ if(mapped)
+ {
+ preferredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
+ }
+
+ // Convert usage to requiredFlags and preferredFlags.
+ switch(pAllocationCreateInfo->usage)
+ {
+ case VMA_MEMORY_USAGE_UNKNOWN:
+ break;
+ case VMA_MEMORY_USAGE_GPU_ONLY:
+ if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
+ {
+ preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
+ }
+ break;
+ case VMA_MEMORY_USAGE_CPU_ONLY:
+ requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
+ break;
+ case VMA_MEMORY_USAGE_CPU_TO_GPU:
+ requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
+ if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
+ {
+ preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
+ }
+ break;
+ case VMA_MEMORY_USAGE_GPU_TO_CPU:
+ requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
+ preferredFlags |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
+ break;
+ default:
+ break;
+ }
+
+ *pMemoryTypeIndex = UINT32_MAX;
+ uint32_t minCost = UINT32_MAX;
+ for(uint32_t memTypeIndex = 0, memTypeBit = 1;
+ memTypeIndex < allocator->GetMemoryTypeCount();
+ ++memTypeIndex, memTypeBit <<= 1)
+ {
+ // This memory type is acceptable according to memoryTypeBits bitmask.
+ if((memTypeBit & memoryTypeBits) != 0)
+ {
+ const VkMemoryPropertyFlags currFlags =
+ allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
+ // This memory type contains requiredFlags.
+ if((requiredFlags & ~currFlags) == 0)
+ {
+ // Calculate cost as number of bits from preferredFlags not present in this memory type.
+ uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags);
+ // Remember memory type with lowest cost.
+ if(currCost < minCost)
+ {
+ *pMemoryTypeIndex = memTypeIndex;
+ if(currCost == 0)
+ {
+ return VK_SUCCESS;
+ }
+ minCost = currCost;
+ }
+ }
+ }
+ }
+ return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
+}
+
+VkResult vmaFindMemoryTypeIndexForBufferInfo(
+ VmaAllocator allocator,
+ const VkBufferCreateInfo* pBufferCreateInfo,
+ const VmaAllocationCreateInfo* pAllocationCreateInfo,
+ uint32_t* pMemoryTypeIndex)
+{
+ VMA_ASSERT(allocator != VK_NULL_HANDLE);
+ VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
+ VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
+ VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
+
+ const VkDevice hDev = allocator->m_hDevice;
+ VkBuffer hBuffer = VK_NULL_HANDLE;
+ VkResult res = allocator->GetVulkanFunctions().vkCreateBuffer(
+ hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
+ if(res == VK_SUCCESS)
+ {
+ VkMemoryRequirements memReq = {};
+ allocator->GetVulkanFunctions().vkGetBufferMemoryRequirements(
+ hDev, hBuffer, &memReq);
+
+ res = vmaFindMemoryTypeIndex(
+ allocator,
+ memReq.memoryTypeBits,
+ pAllocationCreateInfo,
+ pMemoryTypeIndex);
+
+ allocator->GetVulkanFunctions().vkDestroyBuffer(
+ hDev, hBuffer, allocator->GetAllocationCallbacks());
+ }
+ return res;
+}
+
+VkResult vmaFindMemoryTypeIndexForImageInfo(
+ VmaAllocator allocator,
+ const VkImageCreateInfo* pImageCreateInfo,
+ const VmaAllocationCreateInfo* pAllocationCreateInfo,
+ uint32_t* pMemoryTypeIndex)
+{
+ VMA_ASSERT(allocator != VK_NULL_HANDLE);
+ VMA_ASSERT(pImageCreateInfo != VMA_NULL);
+ VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
+ VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
+
+ const VkDevice hDev = allocator->m_hDevice;
+ VkImage hImage = VK_NULL_HANDLE;
+ VkResult res = allocator->GetVulkanFunctions().vkCreateImage(
+ hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
+ if(res == VK_SUCCESS)
+ {
+ VkMemoryRequirements memReq = {};
+ allocator->GetVulkanFunctions().vkGetImageMemoryRequirements(
+ hDev, hImage, &memReq);
+
+ res = vmaFindMemoryTypeIndex(
+ allocator,
+ memReq.memoryTypeBits,
+ pAllocationCreateInfo,
+ pMemoryTypeIndex);
+
+ allocator->GetVulkanFunctions().vkDestroyImage(
+ hDev, hImage, allocator->GetAllocationCallbacks());
+ }
+ return res;
+}
+
+VkResult vmaCreatePool(
+ VmaAllocator allocator,
+ const VmaPoolCreateInfo* pCreateInfo,
+ VmaPool* pPool)
+{
+ VMA_ASSERT(allocator && pCreateInfo && pPool);
+
+ VMA_DEBUG_LOG("vmaCreatePool");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ VkResult res = allocator->CreatePool(pCreateInfo, pPool);
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool);
+ }
+#endif
+
+ return res;
+}
+
+void vmaDestroyPool(
+ VmaAllocator allocator,
+ VmaPool pool)
+{
+ VMA_ASSERT(allocator);
+
+ if(pool == VK_NULL_HANDLE)
+ {
+ return;
+ }
+
+ VMA_DEBUG_LOG("vmaDestroyPool");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool);
+ }
+#endif
+
+ allocator->DestroyPool(pool);
+}
+
+void vmaGetPoolStats(
+ VmaAllocator allocator,
+ VmaPool pool,
+ VmaPoolStats* pPoolStats)
+{
+ VMA_ASSERT(allocator && pool && pPoolStats);
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ allocator->GetPoolStats(pool, pPoolStats);
+}
+
+void vmaMakePoolAllocationsLost(
+ VmaAllocator allocator,
+ VmaPool pool,
+ size_t* pLostAllocationCount)
+{
+ VMA_ASSERT(allocator && pool);
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordMakePoolAllocationsLost(allocator->GetCurrentFrameIndex(), pool);
+ }
+#endif
+
+ allocator->MakePoolAllocationsLost(pool, pLostAllocationCount);
+}
+
+VkResult vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
+{
+ VMA_ASSERT(allocator && pool);
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ VMA_DEBUG_LOG("vmaCheckPoolCorruption");
+
+ return allocator->CheckPoolCorruption(pool);
+}
+
+VkResult vmaAllocateMemory(
+ VmaAllocator allocator,
+ const VkMemoryRequirements* pVkMemoryRequirements,
+ const VmaAllocationCreateInfo* pCreateInfo,
+ VmaAllocation* pAllocation,
+ VmaAllocationInfo* pAllocationInfo)
+{
+ VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
+
+ VMA_DEBUG_LOG("vmaAllocateMemory");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ VkResult result = allocator->AllocateMemory(
+ *pVkMemoryRequirements,
+ false, // requiresDedicatedAllocation
+ false, // prefersDedicatedAllocation
+ VK_NULL_HANDLE, // dedicatedBuffer
+ VK_NULL_HANDLE, // dedicatedImage
+ *pCreateInfo,
+ VMA_SUBALLOCATION_TYPE_UNKNOWN,
+ 1, // allocationCount
+ pAllocation);
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordAllocateMemory(
+ allocator->GetCurrentFrameIndex(),
+ *pVkMemoryRequirements,
+ *pCreateInfo,
+ *pAllocation);
+ }
+#endif
+
+ if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
+ {
+ allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
+ }
+
+ return result;
+}
+
+VkResult vmaAllocateMemoryPages(
+ VmaAllocator allocator,
+ const VkMemoryRequirements* pVkMemoryRequirements,
+ const VmaAllocationCreateInfo* pCreateInfo,
+ size_t allocationCount,
+ VmaAllocation* pAllocations,
+ VmaAllocationInfo* pAllocationInfo)
+{
+ if(allocationCount == 0)
+ {
+ return VK_SUCCESS;
+ }
+
+ VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
+
+ VMA_DEBUG_LOG("vmaAllocateMemoryPages");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ VkResult result = allocator->AllocateMemory(
+ *pVkMemoryRequirements,
+ false, // requiresDedicatedAllocation
+ false, // prefersDedicatedAllocation
+ VK_NULL_HANDLE, // dedicatedBuffer
+ VK_NULL_HANDLE, // dedicatedImage
+ *pCreateInfo,
+ VMA_SUBALLOCATION_TYPE_UNKNOWN,
+ allocationCount,
+ pAllocations);
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordAllocateMemoryPages(
+ allocator->GetCurrentFrameIndex(),
+ *pVkMemoryRequirements,
+ *pCreateInfo,
+ (uint64_t)allocationCount,
+ pAllocations);
+ }
+#endif
+
+ if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
+ {
+ for(size_t i = 0; i < allocationCount; ++i)
+ {
+ allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
+ }
+ }
+
+ return result;
+}
+
+VkResult vmaAllocateMemoryForBuffer(
+ VmaAllocator allocator,
+ VkBuffer buffer,
+ const VmaAllocationCreateInfo* pCreateInfo,
+ VmaAllocation* pAllocation,
+ VmaAllocationInfo* pAllocationInfo)
+{
+ VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
+
+ VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ VkMemoryRequirements vkMemReq = {};
+ bool requiresDedicatedAllocation = false;
+ bool prefersDedicatedAllocation = false;
+ allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
+ requiresDedicatedAllocation,
+ prefersDedicatedAllocation);
+
+ VkResult result = allocator->AllocateMemory(
+ vkMemReq,
+ requiresDedicatedAllocation,
+ prefersDedicatedAllocation,
+ buffer, // dedicatedBuffer
+ VK_NULL_HANDLE, // dedicatedImage
+ *pCreateInfo,
+ VMA_SUBALLOCATION_TYPE_BUFFER,
+ 1, // allocationCount
+ pAllocation);
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordAllocateMemoryForBuffer(
+ allocator->GetCurrentFrameIndex(),
+ vkMemReq,
+ requiresDedicatedAllocation,
+ prefersDedicatedAllocation,
+ *pCreateInfo,
+ *pAllocation);
+ }
+#endif
+
+ if(pAllocationInfo && result == VK_SUCCESS)
+ {
+ allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
+ }
+
+ return result;
+}
+
+VkResult vmaAllocateMemoryForImage(
+ VmaAllocator allocator,
+ VkImage image,
+ const VmaAllocationCreateInfo* pCreateInfo,
+ VmaAllocation* pAllocation,
+ VmaAllocationInfo* pAllocationInfo)
+{
+ VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
+
+ VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ VkMemoryRequirements vkMemReq = {};
+ bool requiresDedicatedAllocation = false;
+ bool prefersDedicatedAllocation = false;
+ allocator->GetImageMemoryRequirements(image, vkMemReq,
+ requiresDedicatedAllocation, prefersDedicatedAllocation);
+
+ VkResult result = allocator->AllocateMemory(
+ vkMemReq,
+ requiresDedicatedAllocation,
+ prefersDedicatedAllocation,
+ VK_NULL_HANDLE, // dedicatedBuffer
+ image, // dedicatedImage
+ *pCreateInfo,
+ VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
+ 1, // allocationCount
+ pAllocation);
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordAllocateMemoryForImage(
+ allocator->GetCurrentFrameIndex(),
+ vkMemReq,
+ requiresDedicatedAllocation,
+ prefersDedicatedAllocation,
+ *pCreateInfo,
+ *pAllocation);
+ }
+#endif
+
+ if(pAllocationInfo && result == VK_SUCCESS)
+ {
+ allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
+ }
+
+ return result;
+}
+
+void vmaFreeMemory(
+ VmaAllocator allocator,
+ VmaAllocation allocation)
+{
+ VMA_ASSERT(allocator);
+
+ if(allocation == VK_NULL_HANDLE)
+ {
+ return;
+ }
+
+ VMA_DEBUG_LOG("vmaFreeMemory");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordFreeMemory(
+ allocator->GetCurrentFrameIndex(),
+ allocation);
+ }
+#endif
+
+ allocator->FreeMemory(
+ 1, // allocationCount
+ &allocation);
+}
+
+void vmaFreeMemoryPages(
+ VmaAllocator allocator,
+ size_t allocationCount,
+ VmaAllocation* pAllocations)
+{
+ if(allocationCount == 0)
+ {
+ return;
+ }
+
+ VMA_ASSERT(allocator);
+
+ VMA_DEBUG_LOG("vmaFreeMemoryPages");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordFreeMemoryPages(
+ allocator->GetCurrentFrameIndex(),
+ (uint64_t)allocationCount,
+ pAllocations);
+ }
+#endif
+
+ allocator->FreeMemory(allocationCount, pAllocations);
+}
+
+VkResult vmaResizeAllocation(
+ VmaAllocator allocator,
+ VmaAllocation allocation,
+ VkDeviceSize newSize)
+{
+ VMA_ASSERT(allocator && allocation);
+
+ VMA_DEBUG_LOG("vmaResizeAllocation");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordResizeAllocation(
+ allocator->GetCurrentFrameIndex(),
+ allocation,
+ newSize);
+ }
+#endif
+
+ return allocator->ResizeAllocation(allocation, newSize);
+}
+
+void vmaGetAllocationInfo(
+ VmaAllocator allocator,
+ VmaAllocation allocation,
+ VmaAllocationInfo* pAllocationInfo)
+{
+ VMA_ASSERT(allocator && allocation && pAllocationInfo);
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordGetAllocationInfo(
+ allocator->GetCurrentFrameIndex(),
+ allocation);
+ }
+#endif
+
+ allocator->GetAllocationInfo(allocation, pAllocationInfo);
+}
+
+VkBool32 vmaTouchAllocation(
+ VmaAllocator allocator,
+ VmaAllocation allocation)
+{
+ VMA_ASSERT(allocator && allocation);
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordTouchAllocation(
+ allocator->GetCurrentFrameIndex(),
+ allocation);
+ }
+#endif
+
+ return allocator->TouchAllocation(allocation);
+}
+
+void vmaSetAllocationUserData(
+ VmaAllocator allocator,
+ VmaAllocation allocation,
+ void* pUserData)
+{
+ VMA_ASSERT(allocator && allocation);
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ allocation->SetUserData(allocator, pUserData);
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordSetAllocationUserData(
+ allocator->GetCurrentFrameIndex(),
+ allocation,
+ pUserData);
+ }
+#endif
+}
+
+void vmaCreateLostAllocation(
+ VmaAllocator allocator,
+ VmaAllocation* pAllocation)
+{
+ VMA_ASSERT(allocator && pAllocation);
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK;
+
+ allocator->CreateLostAllocation(pAllocation);
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordCreateLostAllocation(
+ allocator->GetCurrentFrameIndex(),
+ *pAllocation);
+ }
+#endif
+}
+
+VkResult vmaMapMemory(
+ VmaAllocator allocator,
+ VmaAllocation allocation,
+ void** ppData)
+{
+ VMA_ASSERT(allocator && allocation && ppData);
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ VkResult res = allocator->Map(allocation, ppData);
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordMapMemory(
+ allocator->GetCurrentFrameIndex(),
+ allocation);
+ }
+#endif
+
+ return res;
+}
+
+void vmaUnmapMemory(
+ VmaAllocator allocator,
+ VmaAllocation allocation)
+{
+ VMA_ASSERT(allocator && allocation);
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordUnmapMemory(
+ allocator->GetCurrentFrameIndex(),
+ allocation);
+ }
+#endif
+
+ allocator->Unmap(allocation);
+}
+
+void vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
+{
+ VMA_ASSERT(allocator && allocation);
+
+ VMA_DEBUG_LOG("vmaFlushAllocation");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordFlushAllocation(
+ allocator->GetCurrentFrameIndex(),
+ allocation, offset, size);
+ }
+#endif
+}
+
+void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
+{
+ VMA_ASSERT(allocator && allocation);
+
+ VMA_DEBUG_LOG("vmaInvalidateAllocation");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordInvalidateAllocation(
+ allocator->GetCurrentFrameIndex(),
+ allocation, offset, size);
+ }
+#endif
+}
+
+VkResult vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
+{
+ VMA_ASSERT(allocator);
+
+ VMA_DEBUG_LOG("vmaCheckCorruption");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ return allocator->CheckCorruption(memoryTypeBits);
+}
+
+VkResult vmaDefragment(
+ VmaAllocator allocator,
+ VmaAllocation* pAllocations,
+ size_t allocationCount,
+ VkBool32* pAllocationsChanged,
+ const VmaDefragmentationInfo *pDefragmentationInfo,
+ VmaDefragmentationStats* pDefragmentationStats)
+{
+ // Deprecated interface, reimplemented using new one.
+
+ VmaDefragmentationInfo2 info2 = {};
+ info2.allocationCount = (uint32_t)allocationCount;
+ info2.pAllocations = pAllocations;
+ info2.pAllocationsChanged = pAllocationsChanged;
+ if(pDefragmentationInfo != VMA_NULL)
+ {
+ info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
+ info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove;
+ }
+ else
+ {
+ info2.maxCpuAllocationsToMove = UINT32_MAX;
+ info2.maxCpuBytesToMove = VK_WHOLE_SIZE;
+ }
+ // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero.
+
+ VmaDefragmentationContext ctx;
+ VkResult res = vmaDefragmentationBegin(allocator, &info2, pDefragmentationStats, &ctx);
+ if(res == VK_NOT_READY)
+ {
+ res = vmaDefragmentationEnd( allocator, ctx);
+ }
+ return res;
+}
+
+VkResult vmaDefragmentationBegin(
+ VmaAllocator allocator,
+ const VmaDefragmentationInfo2* pInfo,
+ VmaDefragmentationStats* pStats,
+ VmaDefragmentationContext *pContext)
+{
+ VMA_ASSERT(allocator && pInfo && pContext);
+
+ // Degenerate case: Nothing to defragment.
+ if(pInfo->allocationCount == 0 && pInfo->poolCount == 0)
+ {
+ return VK_SUCCESS;
+ }
+
+ VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL);
+ VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL);
+ VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations));
+ VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools));
+
+ VMA_DEBUG_LOG("vmaDefragmentationBegin");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ VkResult res = allocator->DefragmentationBegin(*pInfo, pStats, pContext);
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordDefragmentationBegin(
+ allocator->GetCurrentFrameIndex(), *pInfo, *pContext);
+ }
+#endif
+
+ return res;
+}
+
+VkResult vmaDefragmentationEnd(
+ VmaAllocator allocator,
+ VmaDefragmentationContext context)
+{
+ VMA_ASSERT(allocator);
+
+ VMA_DEBUG_LOG("vmaDefragmentationEnd");
+
+ if(context != VK_NULL_HANDLE)
+ {
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordDefragmentationEnd(
+ allocator->GetCurrentFrameIndex(), context);
+ }
+#endif
+
+ return allocator->DefragmentationEnd(context);
+ }
+ else
+ {
+ return VK_SUCCESS;
+ }
+}
+
+VkResult vmaBindBufferMemory(
+ VmaAllocator allocator,
+ VmaAllocation allocation,
+ VkBuffer buffer)
+{
+ VMA_ASSERT(allocator && allocation && buffer);
+
+ VMA_DEBUG_LOG("vmaBindBufferMemory");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ return allocator->BindBufferMemory(allocation, buffer);
+}
+
+VkResult vmaBindImageMemory(
+ VmaAllocator allocator,
+ VmaAllocation allocation,
+ VkImage image)
+{
+ VMA_ASSERT(allocator && allocation && image);
+
+ VMA_DEBUG_LOG("vmaBindImageMemory");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ return allocator->BindImageMemory(allocation, image);
+}
+
+VkResult vmaCreateBuffer(
+ VmaAllocator allocator,
+ const VkBufferCreateInfo* pBufferCreateInfo,
+ const VmaAllocationCreateInfo* pAllocationCreateInfo,
+ VkBuffer* pBuffer,
+ VmaAllocation* pAllocation,
+ VmaAllocationInfo* pAllocationInfo)
+{
+ VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
+
+ if(pBufferCreateInfo->size == 0)
+ {
+ return VK_ERROR_VALIDATION_FAILED_EXT;
+ }
+
+ VMA_DEBUG_LOG("vmaCreateBuffer");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ *pBuffer = VK_NULL_HANDLE;
+ *pAllocation = VK_NULL_HANDLE;
+
+ // 1. Create VkBuffer.
+ VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
+ allocator->m_hDevice,
+ pBufferCreateInfo,
+ allocator->GetAllocationCallbacks(),
+ pBuffer);
+ if(res >= 0)
+ {
+ // 2. vkGetBufferMemoryRequirements.
+ VkMemoryRequirements vkMemReq = {};
+ bool requiresDedicatedAllocation = false;
+ bool prefersDedicatedAllocation = false;
+ allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
+ requiresDedicatedAllocation, prefersDedicatedAllocation);
+
+ // Make sure alignment requirements for specific buffer usages reported
+ // in Physical Device Properties are included in alignment reported by memory requirements.
+ if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT) != 0)
+ {
+ VMA_ASSERT(vkMemReq.alignment %
+ allocator->m_PhysicalDeviceProperties.limits.minTexelBufferOffsetAlignment == 0);
+ }
+ if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) != 0)
+ {
+ VMA_ASSERT(vkMemReq.alignment %
+ allocator->m_PhysicalDeviceProperties.limits.minUniformBufferOffsetAlignment == 0);
+ }
+ if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) != 0)
+ {
+ VMA_ASSERT(vkMemReq.alignment %
+ allocator->m_PhysicalDeviceProperties.limits.minStorageBufferOffsetAlignment == 0);
+ }
+
+ // 3. Allocate memory using allocator.
+ res = allocator->AllocateMemory(
+ vkMemReq,
+ requiresDedicatedAllocation,
+ prefersDedicatedAllocation,
+ *pBuffer, // dedicatedBuffer
+ VK_NULL_HANDLE, // dedicatedImage
+ *pAllocationCreateInfo,
+ VMA_SUBALLOCATION_TYPE_BUFFER,
+ 1, // allocationCount
+ pAllocation);
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordCreateBuffer(
+ allocator->GetCurrentFrameIndex(),
+ *pBufferCreateInfo,
+ *pAllocationCreateInfo,
+ *pAllocation);
+ }
+#endif
+
+ if(res >= 0)
+ {
+ // 3. Bind buffer with memory.
+ res = allocator->BindBufferMemory(*pAllocation, *pBuffer);
+ if(res >= 0)
+ {
+ // All steps succeeded.
+ #if VMA_STATS_STRING_ENABLED
+ (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
+ #endif
+ if(pAllocationInfo != VMA_NULL)
+ {
+ allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
+ }
+
+ return VK_SUCCESS;
+ }
+ allocator->FreeMemory(
+ 1, // allocationCount
+ pAllocation);
+ *pAllocation = VK_NULL_HANDLE;
+ (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
+ *pBuffer = VK_NULL_HANDLE;
+ return res;
+ }
+ (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
+ *pBuffer = VK_NULL_HANDLE;
+ return res;
+ }
+ return res;
+}
+
+void vmaDestroyBuffer(
+ VmaAllocator allocator,
+ VkBuffer buffer,
+ VmaAllocation allocation)
+{
+ VMA_ASSERT(allocator);
+
+ if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
+ {
+ return;
+ }
+
+ VMA_DEBUG_LOG("vmaDestroyBuffer");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordDestroyBuffer(
+ allocator->GetCurrentFrameIndex(),
+ allocation);
+ }
+#endif
+
+ if(buffer != VK_NULL_HANDLE)
+ {
+ (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
+ }
+
+ if(allocation != VK_NULL_HANDLE)
+ {
+ allocator->FreeMemory(
+ 1, // allocationCount
+ &allocation);
+ }
+}
+
+VkResult vmaCreateImage(
+ VmaAllocator allocator,
+ const VkImageCreateInfo* pImageCreateInfo,
+ const VmaAllocationCreateInfo* pAllocationCreateInfo,
+ VkImage* pImage,
+ VmaAllocation* pAllocation,
+ VmaAllocationInfo* pAllocationInfo)
+{
+ VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
+
+ if(pImageCreateInfo->extent.width == 0 ||
+ pImageCreateInfo->extent.height == 0 ||
+ pImageCreateInfo->extent.depth == 0 ||
+ pImageCreateInfo->mipLevels == 0 ||
+ pImageCreateInfo->arrayLayers == 0)
+ {
+ return VK_ERROR_VALIDATION_FAILED_EXT;
+ }
+
+ VMA_DEBUG_LOG("vmaCreateImage");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ *pImage = VK_NULL_HANDLE;
+ *pAllocation = VK_NULL_HANDLE;
+
+ // 1. Create VkImage.
+ VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
+ allocator->m_hDevice,
+ pImageCreateInfo,
+ allocator->GetAllocationCallbacks(),
+ pImage);
+ if(res >= 0)
+ {
+ VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
+ VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
+ VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
+
+ // 2. Allocate memory using allocator.
+ VkMemoryRequirements vkMemReq = {};
+ bool requiresDedicatedAllocation = false;
+ bool prefersDedicatedAllocation = false;
+ allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
+ requiresDedicatedAllocation, prefersDedicatedAllocation);
+
+ res = allocator->AllocateMemory(
+ vkMemReq,
+ requiresDedicatedAllocation,
+ prefersDedicatedAllocation,
+ VK_NULL_HANDLE, // dedicatedBuffer
+ *pImage, // dedicatedImage
+ *pAllocationCreateInfo,
+ suballocType,
+ 1, // allocationCount
+ pAllocation);
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordCreateImage(
+ allocator->GetCurrentFrameIndex(),
+ *pImageCreateInfo,
+ *pAllocationCreateInfo,
+ *pAllocation);
+ }
+#endif
+
+ if(res >= 0)
+ {
+ // 3. Bind image with memory.
+ res = allocator->BindImageMemory(*pAllocation, *pImage);
+ if(res >= 0)
+ {
+ // All steps succeeded.
+ #if VMA_STATS_STRING_ENABLED
+ (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);
+ #endif
+ if(pAllocationInfo != VMA_NULL)
+ {
+ allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
+ }
+
+ return VK_SUCCESS;
+ }
+ allocator->FreeMemory(
+ 1, // allocationCount
+ pAllocation);
+ *pAllocation = VK_NULL_HANDLE;
+ (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
+ *pImage = VK_NULL_HANDLE;
+ return res;
+ }
+ (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
+ *pImage = VK_NULL_HANDLE;
+ return res;
+ }
+ return res;
+}
+
+void vmaDestroyImage(
+ VmaAllocator allocator,
+ VkImage image,
+ VmaAllocation allocation)
+{
+ VMA_ASSERT(allocator);
+
+ if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
+ {
+ return;
+ }
+
+ VMA_DEBUG_LOG("vmaDestroyImage");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordDestroyImage(
+ allocator->GetCurrentFrameIndex(),
+ allocation);
+ }
+#endif
+
+ if(image != VK_NULL_HANDLE)
+ {
+ (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
+ }
+ if(allocation != VK_NULL_HANDLE)
+ {
+ allocator->FreeMemory(
+ 1, // allocationCount
+ &allocation);
+ }
+}
+
+#endif // #ifdef VMA_IMPLEMENTATION
diff --git a/src/3rdparty/angle/src/libEGL/libEGL_mingw32.def b/src/3rdparty/angle/src/libEGL/libEGL_mingw32.def
index e68d27295e..14eb331b3a 100644
--- a/src/3rdparty/angle/src/libEGL/libEGL_mingw32.def
+++ b/src/3rdparty/angle/src/libEGL/libEGL_mingw32.def
@@ -1,77 +1,77 @@
LIBRARY libEGL
EXPORTS
- eglBindAPI @14
- eglBindTexImage @20
- eglChooseConfig @7
- eglCopyBuffers @33
- eglCreateContext @23
- eglCreatePbufferFromClientBuffer @18
- eglCreatePbufferSurface @10
- eglCreatePixmapSurface @11
- eglCreateWindowSurface @9
- eglDestroyContext @24
- eglDestroySurface @12
- eglGetConfigAttrib @8
- eglGetConfigs @6
- eglGetCurrentContext @26
- eglGetCurrentDisplay @28
- eglGetCurrentSurface @27
- eglGetDisplay @2
- eglGetError @1
- eglGetProcAddress @34
- eglInitialize @3
- eglMakeCurrent @25
- eglQueryAPI @15
- eglQueryContext @29
- eglQueryString @5
- eglQuerySurface @13
- eglReleaseTexImage @21
- eglReleaseThread @17
- eglSurfaceAttrib @19
- eglSwapBuffers @32
- eglSwapInterval @22
- eglTerminate @4
- eglWaitClient @16
- eglWaitGL @30
- eglWaitNative @31
+ eglBindAPI@4 @14
+ eglBindTexImage@12 @20
+ eglChooseConfig@20 @7
+ eglCopyBuffers@12 @33
+ eglCreateContext@16 @23
+ eglCreatePbufferFromClientBuffer@20 @18
+ eglCreatePbufferSurface@12 @10
+ eglCreatePixmapSurface@16 @11
+ eglCreateWindowSurface@16 @9
+ eglDestroyContext@8 @24
+ eglDestroySurface@8 @12
+ eglGetConfigAttrib@16 @8
+ eglGetConfigs@16 @6
+ eglGetCurrentContext@0 @26
+ eglGetCurrentDisplay@0 @28
+ eglGetCurrentSurface@4 @27
+ eglGetDisplay@4 @2
+ eglGetError@0 @1
+ eglGetProcAddress@4 @34
+ eglInitialize@12 @3
+ eglMakeCurrent@16 @25
+ eglQueryAPI@0 @15
+ eglQueryContext@16 @29
+ eglQueryString@8 @5
+ eglQuerySurface@16 @13
+ eglReleaseTexImage@12 @21
+ eglReleaseThread@0 @17
+ eglSurfaceAttrib@16 @19
+ eglSwapBuffers@8 @32
+ eglSwapInterval@8 @22
+ eglTerminate@4 @4
+ eglWaitClient@0 @16
+ eglWaitGL@0 @30
+ eglWaitNative@4 @31
; Extensions
- eglGetPlatformDisplayEXT @35
- eglQuerySurfacePointerANGLE @36
- eglPostSubBufferNV @37
- eglQueryDisplayAttribEXT @48
- eglQueryDeviceAttribEXT @49
- eglQueryDeviceStringEXT @50
- eglCreateImageKHR @51
- eglDestroyImageKHR @52
- eglCreateDeviceANGLE @53
- eglReleaseDeviceANGLE @54
- eglCreateStreamKHR @55
- eglDestroyStreamKHR @56
- eglStreamAttribKHR @57
- eglQueryStreamKHR @58
- eglQueryStreamu64KHR @59
- eglStreamConsumerGLTextureExternalKHR @60
- eglStreamConsumerAcquireKHR @61
- eglStreamConsumerReleaseKHR @62
- eglStreamConsumerGLTextureExternalAttribsNV @63
- eglCreateStreamProducerD3DTextureNV12ANGLE @64
- eglStreamPostD3DTextureNV12ANGLE @65
- eglGetSyncValuesCHROMIUM @66
- eglSwapBuffersWithDamageEXT @67
- eglProgramCacheGetAttribANGLE @68
- eglProgramCachePopulateANGLE @69
- eglProgramCacheQueryANGLE @70
- eglProgramCacheResizeANGLE @71
+ eglGetPlatformDisplayEXT@12 @35
+ eglQuerySurfacePointerANGLE@16 @36
+ eglPostSubBufferNV@24 @37
+ eglQueryDisplayAttribEXT@12 @48
+ eglQueryDeviceAttribEXT@12 @49
+ eglQueryDeviceStringEXT@8 @50
+ eglCreateImageKHR@20 @51
+ eglDestroyImageKHR@8 @52
+ eglCreateDeviceANGLE@12 @53
+ eglReleaseDeviceANGLE@4 @54
+ eglCreateStreamKHR@8 @55
+ eglDestroyStreamKHR@8 @56
+ eglStreamAttribKHR@16 @57
+ eglQueryStreamKHR@16 @58
+ eglQueryStreamu64KHR@16 @59
+ eglStreamConsumerGLTextureExternalKHR@8 @60
+ eglStreamConsumerAcquireKHR@8 @61
+ eglStreamConsumerReleaseKHR@8 @62
+ eglStreamConsumerGLTextureExternalAttribsNV@12 @63
+ eglCreateStreamProducerD3DTextureNV12ANGLE@12 @64
+ eglStreamPostD3DTextureNV12ANGLE@16 @65
+ eglGetSyncValuesCHROMIUM@20 @66
+ eglSwapBuffersWithDamageEXT@16 @67
+ eglProgramCacheGetAttribANGLE@8 @68
+ eglProgramCachePopulateANGLE@20 @69
+ eglProgramCacheQueryANGLE@24 @70
+ eglProgramCacheResizeANGLE@12 @71
; 1.5 entry points
- eglCreateSync @38
- eglDestroySync @39
- eglClientWaitSync @40
- eglGetSyncAttrib @41
- eglCreateImage @42
- eglDestroyImage @43
- eglGetPlatformDisplay @44
- eglCreatePlatformWindowSurface @45
- eglCreatePlatformPixmapSurface @46
- eglWaitSync @47
+ eglCreateSync@12 @38
+ eglDestroySync@8 @39
+ eglClientWaitSync@20 @40
+ eglGetSyncAttrib@16 @41
+ eglCreateImage@20 @42
+ eglDestroyImage@8 @43
+ eglGetPlatformDisplay@12 @44
+ eglCreatePlatformWindowSurface@16 @45
+ eglCreatePlatformPixmapSurface@16 @46
+ eglWaitSync@12 @47
diff --git a/src/3rdparty/angle/src/libEGL/libEGLd_mingw32.def b/src/3rdparty/angle/src/libEGL/libEGLd_mingw32.def
index e68d27295e..14eb331b3a 100644
--- a/src/3rdparty/angle/src/libEGL/libEGLd_mingw32.def
+++ b/src/3rdparty/angle/src/libEGL/libEGLd_mingw32.def
@@ -1,77 +1,77 @@
LIBRARY libEGL
EXPORTS
- eglBindAPI @14
- eglBindTexImage @20
- eglChooseConfig @7
- eglCopyBuffers @33
- eglCreateContext @23
- eglCreatePbufferFromClientBuffer @18
- eglCreatePbufferSurface @10
- eglCreatePixmapSurface @11
- eglCreateWindowSurface @9
- eglDestroyContext @24
- eglDestroySurface @12
- eglGetConfigAttrib @8
- eglGetConfigs @6
- eglGetCurrentContext @26
- eglGetCurrentDisplay @28
- eglGetCurrentSurface @27
- eglGetDisplay @2
- eglGetError @1
- eglGetProcAddress @34
- eglInitialize @3
- eglMakeCurrent @25
- eglQueryAPI @15
- eglQueryContext @29
- eglQueryString @5
- eglQuerySurface @13
- eglReleaseTexImage @21
- eglReleaseThread @17
- eglSurfaceAttrib @19
- eglSwapBuffers @32
- eglSwapInterval @22
- eglTerminate @4
- eglWaitClient @16
- eglWaitGL @30
- eglWaitNative @31
+ eglBindAPI@4 @14
+ eglBindTexImage@12 @20
+ eglChooseConfig@20 @7
+ eglCopyBuffers@12 @33
+ eglCreateContext@16 @23
+ eglCreatePbufferFromClientBuffer@20 @18
+ eglCreatePbufferSurface@12 @10
+ eglCreatePixmapSurface@16 @11
+ eglCreateWindowSurface@16 @9
+ eglDestroyContext@8 @24
+ eglDestroySurface@8 @12
+ eglGetConfigAttrib@16 @8
+ eglGetConfigs@16 @6
+ eglGetCurrentContext@0 @26
+ eglGetCurrentDisplay@0 @28
+ eglGetCurrentSurface@4 @27
+ eglGetDisplay@4 @2
+ eglGetError@0 @1
+ eglGetProcAddress@4 @34
+ eglInitialize@12 @3
+ eglMakeCurrent@16 @25
+ eglQueryAPI@0 @15
+ eglQueryContext@16 @29
+ eglQueryString@8 @5
+ eglQuerySurface@16 @13
+ eglReleaseTexImage@12 @21
+ eglReleaseThread@0 @17
+ eglSurfaceAttrib@16 @19
+ eglSwapBuffers@8 @32
+ eglSwapInterval@8 @22
+ eglTerminate@4 @4
+ eglWaitClient@0 @16
+ eglWaitGL@0 @30
+ eglWaitNative@4 @31
; Extensions
- eglGetPlatformDisplayEXT @35
- eglQuerySurfacePointerANGLE @36
- eglPostSubBufferNV @37
- eglQueryDisplayAttribEXT @48
- eglQueryDeviceAttribEXT @49
- eglQueryDeviceStringEXT @50
- eglCreateImageKHR @51
- eglDestroyImageKHR @52
- eglCreateDeviceANGLE @53
- eglReleaseDeviceANGLE @54
- eglCreateStreamKHR @55
- eglDestroyStreamKHR @56
- eglStreamAttribKHR @57
- eglQueryStreamKHR @58
- eglQueryStreamu64KHR @59
- eglStreamConsumerGLTextureExternalKHR @60
- eglStreamConsumerAcquireKHR @61
- eglStreamConsumerReleaseKHR @62
- eglStreamConsumerGLTextureExternalAttribsNV @63
- eglCreateStreamProducerD3DTextureNV12ANGLE @64
- eglStreamPostD3DTextureNV12ANGLE @65
- eglGetSyncValuesCHROMIUM @66
- eglSwapBuffersWithDamageEXT @67
- eglProgramCacheGetAttribANGLE @68
- eglProgramCachePopulateANGLE @69
- eglProgramCacheQueryANGLE @70
- eglProgramCacheResizeANGLE @71
+ eglGetPlatformDisplayEXT@12 @35
+ eglQuerySurfacePointerANGLE@16 @36
+ eglPostSubBufferNV@24 @37
+ eglQueryDisplayAttribEXT@12 @48
+ eglQueryDeviceAttribEXT@12 @49
+ eglQueryDeviceStringEXT@8 @50
+ eglCreateImageKHR@20 @51
+ eglDestroyImageKHR@8 @52
+ eglCreateDeviceANGLE@12 @53
+ eglReleaseDeviceANGLE@4 @54
+ eglCreateStreamKHR@8 @55
+ eglDestroyStreamKHR@8 @56
+ eglStreamAttribKHR@16 @57
+ eglQueryStreamKHR@16 @58
+ eglQueryStreamu64KHR@16 @59
+ eglStreamConsumerGLTextureExternalKHR@8 @60
+ eglStreamConsumerAcquireKHR@8 @61
+ eglStreamConsumerReleaseKHR@8 @62
+ eglStreamConsumerGLTextureExternalAttribsNV@12 @63
+ eglCreateStreamProducerD3DTextureNV12ANGLE@12 @64
+ eglStreamPostD3DTextureNV12ANGLE@16 @65
+ eglGetSyncValuesCHROMIUM@20 @66
+ eglSwapBuffersWithDamageEXT@16 @67
+ eglProgramCacheGetAttribANGLE@8 @68
+ eglProgramCachePopulateANGLE@20 @69
+ eglProgramCacheQueryANGLE@24 @70
+ eglProgramCacheResizeANGLE@12 @71
; 1.5 entry points
- eglCreateSync @38
- eglDestroySync @39
- eglClientWaitSync @40
- eglGetSyncAttrib @41
- eglCreateImage @42
- eglDestroyImage @43
- eglGetPlatformDisplay @44
- eglCreatePlatformWindowSurface @45
- eglCreatePlatformPixmapSurface @46
- eglWaitSync @47
+ eglCreateSync@12 @38
+ eglDestroySync@8 @39
+ eglClientWaitSync@20 @40
+ eglGetSyncAttrib@16 @41
+ eglCreateImage@20 @42
+ eglDestroyImage@8 @43
+ eglGetPlatformDisplay@12 @44
+ eglCreatePlatformWindowSurface@16 @45
+ eglCreatePlatformPixmapSurface@16 @46
+ eglWaitSync@12 @47
diff --git a/src/3rdparty/angle/src/libGLESv2/libGLESv2_mingw32.def b/src/3rdparty/angle/src/libGLESv2/libGLESv2_mingw32.def
index 2ff4cc0579..a182c21a05 100644
--- a/src/3rdparty/angle/src/libGLESv2/libGLESv2_mingw32.def
+++ b/src/3rdparty/angle/src/libGLESv2/libGLESv2_mingw32.def
@@ -1,412 +1,411 @@
LIBRARY libGLESv2
EXPORTS
- glActiveTexture @1
- glAttachShader @2
- glBindAttribLocation @3
- glBindBuffer @4
- glBindFramebuffer @5
- glBindRenderbuffer @6
- glBindTexture @7
- glBlendColor @8
- glBlendEquation @9
- glBlendEquationSeparate @10
- glBlendFunc @11
- glBlendFuncSeparate @12
- glBufferData @13
- glBufferSubData @14
- glCheckFramebufferStatus @15
- glClear @16
- glClearColor @17
- glClearDepthf @18
- glClearStencil @19
- glColorMask @20
- glCompileShader @21
- glCompressedTexImage2D @22
- glCompressedTexSubImage2D @23
- glCopyTexImage2D @24
- glCopyTexSubImage2D @25
- glCreateProgram @26
- glCreateShader @27
- glCullFace @28
- glDeleteBuffers @29
- glDeleteFramebuffers @30
- glDeleteProgram @32
- glDeleteRenderbuffers @33
- glDeleteShader @34
- glDeleteTextures @31
- glDepthFunc @36
- glDepthMask @37
- glDepthRangef @38
- glDetachShader @35
- glDisable @39
- glDisableVertexAttribArray @40
- glDrawArrays @41
- glDrawElements @42
- glEnable @43
- glEnableVertexAttribArray @44
- glFinish @45
- glFlush @46
- glFramebufferRenderbuffer @47
- glFramebufferTexture2D @48
- glFrontFace @49
- glGenBuffers @50
- glGenFramebuffers @52
- glGenRenderbuffers @53
- glGenTextures @54
- glGenerateMipmap @51
- glGetActiveAttrib @55
- glGetActiveUniform @56
- glGetAttachedShaders @57
- glGetAttribLocation @58
- glGetBooleanv @59
- glGetBufferParameteriv @60
- glGetError @61
- glGetFloatv @62
- glGetFramebufferAttachmentParameteriv @63
- glGetIntegerv @64
- glGetProgramInfoLog @66
- glGetProgramiv @65
- glGetRenderbufferParameteriv @67
- glGetShaderInfoLog @69
- glGetShaderPrecisionFormat @70
- glGetShaderSource @71
- glGetShaderiv @68
- glGetString @72
- glGetTexParameterfv @73
- glGetTexParameteriv @74
- glGetUniformLocation @77
- glGetUniformfv @75
- glGetUniformiv @76
- glGetVertexAttribPointerv @80
- glGetVertexAttribfv @78
- glGetVertexAttribiv @79
- glHint @81
- glIsBuffer @82
- glIsEnabled @83
- glIsFramebuffer @84
- glIsProgram @85
- glIsRenderbuffer @86
- glIsShader @87
- glIsTexture @88
- glLineWidth @89
- glLinkProgram @90
- glPixelStorei @91
- glPolygonOffset @92
- glReadPixels @93
- glReleaseShaderCompiler @94
- glRenderbufferStorage @95
- glSampleCoverage @96
- glScissor @97
- glShaderBinary @98
- glShaderSource @99
- glStencilFunc @100
- glStencilFuncSeparate @101
- glStencilMask @102
- glStencilMaskSeparate @103
- glStencilOp @104
- glStencilOpSeparate @105
- glTexImage2D @106
- glTexParameterf @107
- glTexParameterfv @108
- glTexParameteri @109
- glTexParameteriv @110
- glTexSubImage2D @111
- glUniform1f @112
- glUniform1fv @113
- glUniform1i @114
- glUniform1iv @115
- glUniform2f @116
- glUniform2fv @117
- glUniform2i @118
- glUniform2iv @119
- glUniform3f @120
- glUniform3fv @121
- glUniform3i @122
- glUniform3iv @123
- glUniform4f @124
- glUniform4fv @125
- glUniform4i @126
- glUniform4iv @127
- glUniformMatrix2fv @128
- glUniformMatrix3fv @129
- glUniformMatrix4fv @130
- glUseProgram @131
- glValidateProgram @132
- glVertexAttrib1f @133
- glVertexAttrib1fv @134
- glVertexAttrib2f @135
- glVertexAttrib2fv @136
- glVertexAttrib3f @137
- glVertexAttrib3fv @138
- glVertexAttrib4f @139
- glVertexAttrib4fv @140
- glVertexAttribPointer @141
- glViewport @142
+ glActiveTexture@4 @1
+ glAttachShader@8 @2
+ glBindAttribLocation@12 @3
+ glBindBuffer@8 @4
+ glBindFramebuffer@8 @5
+ glBindRenderbuffer@8 @6
+ glBindTexture@8 @7
+ glBlendColor@16 @8
+ glBlendEquation@4 @9
+ glBlendEquationSeparate@8 @10
+ glBlendFunc@8 @11
+ glBlendFuncSeparate@16 @12
+ glBufferData@16 @13
+ glBufferSubData@16 @14
+ glCheckFramebufferStatus@4 @15
+ glClear@4 @16
+ glClearColor@16 @17
+ glClearDepthf@4 @18
+ glClearStencil@4 @19
+ glColorMask@16 @20
+ glCompileShader@4 @21
+ glCompressedTexImage2D@32 @22
+ glCompressedTexSubImage2D@36 @23
+ glCopyTexImage2D@32 @24
+ glCopyTexSubImage2D@32 @25
+ glCreateProgram@0 @26
+ glCreateShader@4 @27
+ glCullFace@4 @28
+ glDeleteBuffers@8 @29
+ glDeleteFramebuffers@8 @30
+ glDeleteProgram@4 @32
+ glDeleteRenderbuffers@8 @33
+ glDeleteShader@4 @34
+ glDeleteTextures@8 @31
+ glDepthFunc@4 @36
+ glDepthMask@4 @37
+ glDepthRangef@8 @38
+ glDetachShader@8 @35
+ glDisable@4 @39
+ glDisableVertexAttribArray@4 @40
+ glDrawArrays@12 @41
+ glDrawElements@16 @42
+ glEnable@4 @43
+ glEnableVertexAttribArray@4 @44
+ glFinish@0 @45
+ glFlush@0 @46
+ glFramebufferRenderbuffer@16 @47
+ glFramebufferTexture2D@20 @48
+ glFrontFace@4 @49
+ glGenBuffers@8 @50
+ glGenFramebuffers@8 @52
+ glGenRenderbuffers@8 @53
+ glGenTextures@8 @54
+ glGenerateMipmap@4 @51
+ glGetActiveAttrib@28 @55
+ glGetActiveUniform@28 @56
+ glGetAttachedShaders@16 @57
+ glGetAttribLocation@8 @58
+ glGetBooleanv@8 @59
+ glGetBufferParameteriv@12 @60
+ glGetError@0 @61
+ glGetFloatv@8 @62
+ glGetFramebufferAttachmentParameteriv@16 @63
+ glGetIntegerv@8 @64
+ glGetProgramInfoLog@16 @66
+ glGetProgramiv@12 @65
+ glGetRenderbufferParameteriv@12 @67
+ glGetShaderInfoLog@16 @69
+ glGetShaderPrecisionFormat@16 @70
+ glGetShaderSource@16 @71
+ glGetShaderiv@12 @68
+ glGetString@4 @72
+ glGetTexParameterfv@12 @73
+ glGetTexParameteriv@12 @74
+ glGetUniformLocation@8 @77
+ glGetUniformfv@12 @75
+ glGetUniformiv@12 @76
+ glGetVertexAttribPointerv@12 @80
+ glGetVertexAttribfv@12 @78
+ glGetVertexAttribiv@12 @79
+ glHint@8 @81
+ glIsBuffer@4 @82
+ glIsEnabled@4 @83
+ glIsFramebuffer@4 @84
+ glIsProgram@4 @85
+ glIsRenderbuffer@4 @86
+ glIsShader@4 @87
+ glIsTexture@4 @88
+ glLineWidth@4 @89
+ glLinkProgram@4 @90
+ glPixelStorei@8 @91
+ glPolygonOffset@8 @92
+ glReadPixels@28 @93
+ glReleaseShaderCompiler@0 @94
+ glRenderbufferStorage@16 @95
+ glSampleCoverage@8 @96
+ glScissor@16 @97
+ glShaderBinary@20 @98
+ glShaderSource@16 @99
+ glStencilFunc@12 @100
+ glStencilFuncSeparate@16 @101
+ glStencilMask@4 @102
+ glStencilMaskSeparate@8 @103
+ glStencilOp@12 @104
+ glStencilOpSeparate@16 @105
+ glTexImage2D@36 @106
+ glTexParameterf@12 @107
+ glTexParameterfv@12 @108
+ glTexParameteri@12 @109
+ glTexParameteriv@12 @110
+ glTexSubImage2D@36 @111
+ glUniform1f@8 @112
+ glUniform1fv@12 @113
+ glUniform1i@8 @114
+ glUniform1iv@12 @115
+ glUniform2f@12 @116
+ glUniform2fv@12 @117
+ glUniform2i@12 @118
+ glUniform2iv@12 @119
+ glUniform3f@16 @120
+ glUniform3fv@12 @121
+ glUniform3i@16 @122
+ glUniform3iv@12 @123
+ glUniform4f@20 @124
+ glUniform4fv@12 @125
+ glUniform4i@20 @126
+ glUniform4iv@12 @127
+ glUniformMatrix2fv@16 @128
+ glUniformMatrix3fv@16 @129
+ glUniformMatrix4fv@16 @130
+ glUseProgram@4 @131
+ glValidateProgram@4 @132
+ glVertexAttrib1f@8 @133
+ glVertexAttrib1fv@8 @134
+ glVertexAttrib2f@12 @135
+ glVertexAttrib2fv@8 @136
+ glVertexAttrib3f@16 @137
+ glVertexAttrib3fv@8 @138
+ glVertexAttrib4f@20 @139
+ glVertexAttrib4fv@8 @140
+ glVertexAttribPointer@24 @141
+ glViewport@16 @142
; Extensions
- glBlitFramebufferANGLE @149
- glRenderbufferStorageMultisampleANGLE @150
- glDeleteFencesNV @151
- glFinishFenceNV @152
- glGenFencesNV @153
- glGetFenceivNV @154
- glIsFenceNV @155
- glSetFenceNV @156
- glTestFenceNV @157
- glGetTranslatedShaderSourceANGLE @159
- glTexStorage2DEXT @160
- glGetGraphicsResetStatusEXT @161
- glReadnPixelsEXT @162
- glGetnUniformfvEXT @163
- glGetnUniformivEXT @164
- glGenQueriesEXT @165
- glDeleteQueriesEXT @166
- glIsQueryEXT @167
- glBeginQueryEXT @168
- glEndQueryEXT @169
- glGetQueryivEXT @170
- glGetQueryObjectuivEXT @171
- glVertexAttribDivisorANGLE @172
- glDrawArraysInstancedANGLE @173
- glDrawElementsInstancedANGLE @174
- glProgramBinaryOES @175
- glGetProgramBinaryOES @176
- glDrawBuffersEXT @179
- glMapBufferOES @285
- glUnmapBufferOES @286
- glGetBufferPointervOES @287
- glMapBufferRangeEXT @288
- glFlushMappedBufferRangeEXT @289
- glDiscardFramebufferEXT @293
- glInsertEventMarkerEXT @294
- glPushGroupMarkerEXT @295
- glPopGroupMarkerEXT @296
- glEGLImageTargetTexture2DOES @297
- glEGLImageTargetRenderbufferStorageOES @298
- glBindVertexArrayOES @299
- glDeleteVertexArraysOES @300
- glGenVertexArraysOES @301
- glIsVertexArrayOES @302
- glDebugMessageControlKHR @303
- glDebugMessageInsertKHR @304
- glDebugMessageCallbackKHR @305
- glGetDebugMessageLogKHR @306
- glPushDebugGroupKHR @307
- glPopDebugGroupKHR @308
- glObjectLabelKHR @309
- glGetObjectLabelKHR @310
- glObjectPtrLabelKHR @311
- glGetObjectPtrLabelKHR @312
- glGetPointervKHR @313
- glQueryCounterEXT @314
- glGetQueryObjectivEXT @315
- glGetQueryObjecti64vEXT @316
- glGetQueryObjectui64vEXT @317
- glBindUniformLocationCHROMIUM @318
- glCoverageModulationCHROMIUM @319
+ glBlitFramebufferANGLE@40 @149
+ glRenderbufferStorageMultisampleANGLE@20 @150
+ glDeleteFencesNV@8 @151
+ glFinishFenceNV@4 @152
+ glGenFencesNV@8 @153
+ glGetFenceivNV@12 @154
+ glIsFenceNV@4 @155
+ glSetFenceNV@8 @156
+ glTestFenceNV@4 @157
+ glGetTranslatedShaderSourceANGLE@16 @159
+ glTexStorage2DEXT@20 @160
+ glGetGraphicsResetStatusEXT@0 @161
+ glReadnPixelsEXT@32 @162
+ glGetnUniformfvEXT@16 @163
+ glGetnUniformivEXT@16 @164
+ glGenQueriesEXT@8 @165
+ glDeleteQueriesEXT@8 @166
+ glIsQueryEXT@4 @167
+ glBeginQueryEXT@8 @168
+ glEndQueryEXT@4 @169
+ glGetQueryivEXT@12 @170
+ glGetQueryObjectuivEXT@12 @171
+ glVertexAttribDivisorANGLE@8 @172
+ glDrawArraysInstancedANGLE@16 @173
+ glDrawElementsInstancedANGLE@20 @174
+ glProgramBinaryOES@16 @175
+ glGetProgramBinaryOES@20 @176
+ glDrawBuffersEXT@8 @179
+ glMapBufferOES@8 @285
+ glUnmapBufferOES@4 @286
+ glGetBufferPointervOES@12 @287
+ glMapBufferRangeEXT@16 @288
+ glFlushMappedBufferRangeEXT@12 @289
+ glDiscardFramebufferEXT@12 @293
+ glInsertEventMarkerEXT@8 @294
+ glPushGroupMarkerEXT@8 @295
+ glPopGroupMarkerEXT@0 @296
+ glEGLImageTargetTexture2DOES@8 @297
+ glEGLImageTargetRenderbufferStorageOES@8 @298
+ glBindVertexArrayOES@4 @299
+ glDeleteVertexArraysOES@8 @300
+ glGenVertexArraysOES@8 @301
+ glIsVertexArrayOES@4 @302
+ glDebugMessageControlKHR@24 @303
+ glDebugMessageInsertKHR@24 @304
+ glDebugMessageCallbackKHR@8 @305
+ glGetDebugMessageLogKHR@32 @306
+ glPushDebugGroupKHR@16 @307
+ glPopDebugGroupKHR@0 @308
+ glObjectLabelKHR@16 @309
+ glGetObjectLabelKHR@20 @310
+ glObjectPtrLabelKHR@12 @311
+ glGetObjectPtrLabelKHR@16 @312
+ glGetPointervKHR@8 @313
+ glQueryCounterEXT@8 @314
+ glGetQueryObjectivEXT@12 @315
+ glGetQueryObjecti64vEXT@12 @316
+ glGetQueryObjectui64vEXT@12 @317
+ glBindUniformLocationCHROMIUM@12 @318
+ glCoverageModulationCHROMIUM@4 @319
+ glMatrixLoadfCHROMIUM@8 @320
+ glMatrixLoadIdentityCHROMIUM@4 @321
+ glGenPathsCHROMIUM@4 @322
+ glDeletePathsCHROMIUM@8 @323
+ glIsPathCHROMIUM@4 @324
+ glPathCommandsCHROMIUM@24 @325
+ glPathParameterfCHROMIUM@12 @326
+ glPathParameteriCHROMIUM@12 @327
+ glGetPathParameterfvCHROMIUM@12 @328
+ glGetPathParameterivCHROMIUM@12 @329
+ glPathStencilFuncCHROMIUM@12 @330
+ glStencilFillPathCHROMIUM@12 @331
+ glStencilStrokePathCHROMIUM@12 @332
+ glCoverFillPathCHROMIUM@8 @333
+ glCoverStrokePathCHROMIUM@8 @334
+ glStencilThenCoverFillPathCHROMIUM@16 @335
+ glStencilThenCoverStrokePathCHROMIUM@16 @336
+ glCoverFillPathInstancedCHROMIUM@28 @337
+ glCoverStrokePathInstancedCHROMIUM@28 @338
+ glStencilStrokePathInstancedCHROMIUM@32 @339
+ glStencilFillPathInstancedCHROMIUM@32 @340
+ glStencilThenCoverFillPathInstancedCHROMIUM@36 @341
+ glStencilThenCoverStrokePathInstancedCHROMIUM@36 @342
+ glBindFragmentInputLocationCHROMIUM@12 @343
+ glProgramPathFragmentInputGenCHROMIUM@20 @344
- glMatrixLoadfCHROMIUM @320
- glMatrixLoadIdentityCHROMIUM @321
- glGenPathsCHROMIUM @322
- glDeletePathsCHROMIUM @323
- glIsPathCHROMIUM @324
- glPathCommandsCHROMIUM @325
- glPathParameterfCHROMIUM @326
- glPathParameteriCHROMIUM @327
- glGetPathParameterfvCHROMIUM @328
- glGetPathParameterivCHROMIUM @329
- glPathStencilFuncCHROMIUM @330
- glStencilFillPathCHROMIUM @331
- glStencilStrokePathCHROMIUM @332
- glCoverFillPathCHROMIUM @333
- glCoverStrokePathCHROMIUM @334
- glStencilThenCoverFillPathCHROMIUM @335
- glStencilThenCoverStrokePathCHROMIUM @336
- glCoverFillPathInstancedCHROMIUM @337
- glCoverStrokePathInstancedCHROMIUM @338
- glStencilStrokePathInstancedCHROMIUM @339
- glStencilFillPathInstancedCHROMIUM @340
- glStencilThenCoverFillPathInstancedCHROMIUM @341
- glStencilThenCoverStrokePathInstancedCHROMIUM @342
- glBindFragmentInputLocationCHROMIUM @343
- glProgramPathFragmentInputGenCHROMIUM @344
-
- glFramebufferTextureMultiviewLayeredANGLE @413
- glFramebufferTextureMultiviewSideBySideANGLE @414
- glRequestExtensionANGLE @415
+ glFramebufferTextureMultiviewLayeredANGLE@24 @413
+ glFramebufferTextureMultiviewSideBySideANGLE@24 @414
+ glRequestExtensionANGLE@4 @415
; GLES 3.0 Functions
- glReadBuffer @180
- glDrawRangeElements @181
- glTexImage3D @182
- glTexSubImage3D @183
- glCopyTexSubImage3D @184
- glCompressedTexImage3D @185
- glCompressedTexSubImage3D @186
- glGenQueries @187
- glDeleteQueries @188
- glIsQuery @189
- glBeginQuery @190
- glEndQuery @191
- glGetQueryiv @192
- glGetQueryObjectuiv @193
- glUnmapBuffer @194
- glGetBufferPointerv @195
- glDrawBuffers @196
- glUniformMatrix2x3fv @197
- glUniformMatrix3x2fv @198
- glUniformMatrix2x4fv @199
- glUniformMatrix4x2fv @200
- glUniformMatrix3x4fv @201
- glUniformMatrix4x3fv @202
- glBlitFramebuffer @203
- glRenderbufferStorageMultisample @204
- glFramebufferTextureLayer @205
- glMapBufferRange @206
- glFlushMappedBufferRange @207
- glBindVertexArray @208
- glDeleteVertexArrays @209
- glGenVertexArrays @210
- glIsVertexArray @211
- glGetIntegeri_v @212
- glBeginTransformFeedback @213
- glEndTransformFeedback @214
- glBindBufferRange @215
- glBindBufferBase @216
- glTransformFeedbackVaryings @217
- glGetTransformFeedbackVarying @218
- glVertexAttribIPointer @219
- glGetVertexAttribIiv @220
- glGetVertexAttribIuiv @221
- glVertexAttribI4i @222
- glVertexAttribI4ui @223
- glVertexAttribI4iv @224
- glVertexAttribI4uiv @225
- glGetUniformuiv @226
- glGetFragDataLocation @227
- glUniform1ui @228
- glUniform2ui @229
- glUniform3ui @230
- glUniform4ui @231
- glUniform1uiv @232
- glUniform2uiv @233
- glUniform3uiv @234
- glUniform4uiv @235
- glClearBufferiv @236
- glClearBufferuiv @237
- glClearBufferfv @238
- glClearBufferfi @239
- glGetStringi @240
- glCopyBufferSubData @241
- glGetUniformIndices @242
- glGetActiveUniformsiv @243
- glGetUniformBlockIndex @244
- glGetActiveUniformBlockiv @245
- glGetActiveUniformBlockName @246
- glUniformBlockBinding @247
- glDrawArraysInstanced @248
- glDrawElementsInstanced @249
- glFenceSync @250
- glIsSync @251
- glDeleteSync @252
- glClientWaitSync @253
- glWaitSync @254
- glGetInteger64v @255
- glGetSynciv @256
- glGetInteger64i_v @257
- glGetBufferParameteri64v @258
- glGenSamplers @259
- glDeleteSamplers @260
- glIsSampler @261
- glBindSampler @262
- glSamplerParameteri @263
- glSamplerParameteriv @264
- glSamplerParameterf @265
- glSamplerParameterfv @266
- glGetSamplerParameteriv @267
- glGetSamplerParameterfv @268
- glVertexAttribDivisor @269
- glBindTransformFeedback @270
- glDeleteTransformFeedbacks @271
- glGenTransformFeedbacks @272
- glIsTransformFeedback @273
- glPauseTransformFeedback @274
- glResumeTransformFeedback @275
- glGetProgramBinary @276
- glProgramBinary @277
- glProgramParameteri @278
- glInvalidateFramebuffer @279
- glInvalidateSubFramebuffer @280
- glTexStorage2D @281
- glTexStorage3D @282
- glGetInternalformativ @283
+ glReadBuffer@4 @180
+ glDrawRangeElements@24 @181
+ glTexImage3D@40 @182
+ glTexSubImage3D@44 @183
+ glCopyTexSubImage3D@36 @184
+ glCompressedTexImage3D@36 @185
+ glCompressedTexSubImage3D@44 @186
+ glGenQueries@8 @187
+ glDeleteQueries@8 @188
+ glIsQuery@4 @189
+ glBeginQuery@8 @190
+ glEndQuery@4 @191
+ glGetQueryiv@12 @192
+ glGetQueryObjectuiv@12 @193
+ glUnmapBuffer@4 @194
+ glGetBufferPointerv@12 @195
+ glDrawBuffers@8 @196
+ glUniformMatrix2x3fv@16 @197
+ glUniformMatrix3x2fv@16 @198
+ glUniformMatrix2x4fv@16 @199
+ glUniformMatrix4x2fv@16 @200
+ glUniformMatrix3x4fv@16 @201
+ glUniformMatrix4x3fv@16 @202
+ glBlitFramebuffer@40 @203
+ glRenderbufferStorageMultisample@20 @204
+ glFramebufferTextureLayer@20 @205
+ glMapBufferRange@16 @206
+ glFlushMappedBufferRange@12 @207
+ glBindVertexArray@4 @208
+ glDeleteVertexArrays@8 @209
+ glGenVertexArrays@8 @210
+ glIsVertexArray@4 @211
+ glGetIntegeri_v@12 @212
+ glBeginTransformFeedback@4 @213
+ glEndTransformFeedback@0 @214
+ glBindBufferRange@20 @215
+ glBindBufferBase@12 @216
+ glTransformFeedbackVaryings@16 @217
+ glGetTransformFeedbackVarying@28 @218
+ glVertexAttribIPointer@20 @219
+ glGetVertexAttribIiv@12 @220
+ glGetVertexAttribIuiv@12 @221
+ glVertexAttribI4i@20 @222
+ glVertexAttribI4ui@20 @223
+ glVertexAttribI4iv@8 @224
+ glVertexAttribI4uiv@8 @225
+ glGetUniformuiv@12 @226
+ glGetFragDataLocation@8 @227
+ glUniform1ui@8 @228
+ glUniform2ui@12 @229
+ glUniform3ui@16 @230
+ glUniform4ui@20 @231
+ glUniform1uiv@12 @232
+ glUniform2uiv@12 @233
+ glUniform3uiv@12 @234
+ glUniform4uiv@12 @235
+ glClearBufferiv@12 @236
+ glClearBufferuiv@12 @237
+ glClearBufferfv@12 @238
+ glClearBufferfi@16 @239
+ glGetStringi@8 @240
+ glCopyBufferSubData@20 @241
+ glGetUniformIndices@16 @242
+ glGetActiveUniformsiv@20 @243
+ glGetUniformBlockIndex@8 @244
+ glGetActiveUniformBlockiv@16 @245
+ glGetActiveUniformBlockName@20 @246
+ glUniformBlockBinding@12 @247
+ glDrawArraysInstanced@16 @248
+ glDrawElementsInstanced@20 @249
+ glFenceSync@8 @250
+ glIsSync@4 @251
+ glDeleteSync@4 @252
+ glClientWaitSync@16 @253
+ glWaitSync@16 @254
+ glGetInteger64v@8 @255
+ glGetSynciv@20 @256
+ glGetInteger64i_v@12 @257
+ glGetBufferParameteri64v@12 @258
+ glGenSamplers@8 @259
+ glDeleteSamplers@8 @260
+ glIsSampler@4 @261
+ glBindSampler@8 @262
+ glSamplerParameteri@12 @263
+ glSamplerParameteriv@12 @264
+ glSamplerParameterf@12 @265
+ glSamplerParameterfv@12 @266
+ glGetSamplerParameteriv@12 @267
+ glGetSamplerParameterfv@12 @268
+ glVertexAttribDivisor@8 @269
+ glBindTransformFeedback@8 @270
+ glDeleteTransformFeedbacks@8 @271
+ glGenTransformFeedbacks@8 @272
+ glIsTransformFeedback@4 @273
+ glPauseTransformFeedback@0 @274
+ glResumeTransformFeedback@0 @275
+ glGetProgramBinary@20 @276
+ glProgramBinary@16 @277
+ glProgramParameteri@12 @278
+ glInvalidateFramebuffer@12 @279
+ glInvalidateSubFramebuffer@28 @280
+ glTexStorage2D@20 @281
+ glTexStorage3D@24 @282
+ glGetInternalformativ@20 @283
; GLES 3.1 Functions
- glDispatchCompute @345
- glDispatchComputeIndirect @346
- glDrawArraysIndirect @347
- glDrawElementsIndirect @348
- glFramebufferParameteri @349
- glGetFramebufferParameteriv @350
- glGetProgramInterfaceiv @351
- glGetProgramResourceIndex @352
- glGetProgramResourceName @353
- glGetProgramResourceiv @354
- glGetProgramResourceLocation @355
- glUseProgramStages @356
- glActiveShaderProgram @357
- glCreateShaderProgramv @358
- glBindProgramPipeline @359
- glDeleteProgramPipelines @360
- glGenProgramPipelines @361
- glIsProgramPipeline @362
- glGetProgramPipelineiv @363
- glProgramUniform1i @364
- glProgramUniform2i @365
- glProgramUniform3i @366
- glProgramUniform4i @367
- glProgramUniform1ui @368
- glProgramUniform2ui @369
- glProgramUniform3ui @370
- glProgramUniform4ui @371
- glProgramUniform1f @372
- glProgramUniform2f @373
- glProgramUniform3f @374
- glProgramUniform4f @375
- glProgramUniform1iv @376
- glProgramUniform2iv @377
- glProgramUniform3iv @378
- glProgramUniform4iv @379
- glProgramUniform1uiv @380
- glProgramUniform2uiv @381
- glProgramUniform3uiv @382
- glProgramUniform4uiv @383
- glProgramUniform1fv @384
- glProgramUniform2fv @385
- glProgramUniform3fv @386
- glProgramUniform4fv @387
- glProgramUniformMatrix2fv @388
- glProgramUniformMatrix3fv @389
- glProgramUniformMatrix4fv @390
- glProgramUniformMatrix2x3fv @391
- glProgramUniformMatrix3x2fv @392
- glProgramUniformMatrix2x4fv @393
- glProgramUniformMatrix4x2fv @394
- glProgramUniformMatrix3x4fv @395
- glProgramUniformMatrix4x3fv @396
- glValidateProgramPipeline @397
- glGetProgramPipelineInfoLog @398
- glBindImageTexture @399
- glGetBooleani_v @400
- glMemoryBarrier @401
- glMemoryBarrierByRegion @402
- glTexStorage2DMultisample @403
- glGetMultisamplefv @404
- glSampleMaski @405
- glGetTexLevelParameteriv @406
- glGetTexLevelParameterfv @407
- glBindVertexBuffer @408
- glVertexAttribFormat @409
- glVertexAttribIFormat @410
- glVertexAttribBinding @411
- glVertexBindingDivisor @412
+ glDispatchCompute@12 @345
+ glDispatchComputeIndirect@4 @346
+ glDrawArraysIndirect@8 @347
+ glDrawElementsIndirect@12 @348
+ glFramebufferParameteri@12 @349
+ glGetFramebufferParameteriv@12 @350
+ glGetProgramInterfaceiv@16 @351
+ glGetProgramResourceIndex@12 @352
+ glGetProgramResourceName@24 @353
+ glGetProgramResourceiv@32 @354
+ glGetProgramResourceLocation@12 @355
+ glUseProgramStages@12 @356
+ glActiveShaderProgram@8 @357
+ glCreateShaderProgramv@12 @358
+ glBindProgramPipeline@4 @359
+ glDeleteProgramPipelines@8 @360
+ glGenProgramPipelines@8 @361
+ glIsProgramPipeline@4 @362
+ glGetProgramPipelineiv@12 @363
+ glProgramUniform1i@12 @364
+ glProgramUniform2i@16 @365
+ glProgramUniform3i@20 @366
+ glProgramUniform4i@24 @367
+ glProgramUniform1ui@12 @368
+ glProgramUniform2ui@16 @369
+ glProgramUniform3ui@20 @370
+ glProgramUniform4ui@24 @371
+ glProgramUniform1f@12 @372
+ glProgramUniform2f@16 @373
+ glProgramUniform3f@20 @374
+ glProgramUniform4f@24 @375
+ glProgramUniform1iv@16 @376
+ glProgramUniform2iv@16 @377
+ glProgramUniform3iv@16 @378
+ glProgramUniform4iv@16 @379
+ glProgramUniform1uiv@16 @380
+ glProgramUniform2uiv@16 @381
+ glProgramUniform3uiv@16 @382
+ glProgramUniform4uiv@16 @383
+ glProgramUniform1fv@16 @384
+ glProgramUniform2fv@16 @385
+ glProgramUniform3fv@16 @386
+ glProgramUniform4fv@16 @387
+ glProgramUniformMatrix2fv@20 @388
+ glProgramUniformMatrix3fv@20 @389
+ glProgramUniformMatrix4fv@20 @390
+ glProgramUniformMatrix2x3fv@20 @391
+ glProgramUniformMatrix3x2fv@20 @392
+ glProgramUniformMatrix2x4fv@20 @393
+ glProgramUniformMatrix4x2fv@20 @394
+ glProgramUniformMatrix3x4fv@20 @395
+ glProgramUniformMatrix4x3fv@20 @396
+ glValidateProgramPipeline@4 @397
+ glGetProgramPipelineInfoLog@16 @398
+ glBindImageTexture@28 @399
+ glGetBooleani_v@12 @400
+ glMemoryBarrier@4 @401
+ glMemoryBarrierByRegion@4 @402
+ glTexStorage2DMultisample@24 @403
+ glGetMultisamplefv@12 @404
+ glSampleMaski@8 @405
+ glGetTexLevelParameteriv@16 @406
+ glGetTexLevelParameterfv@16 @407
+ glBindVertexBuffer@16 @408
+ glVertexAttribFormat@20 @409
+ glVertexAttribIFormat@16 @410
+ glVertexAttribBinding@8 @411
+ glVertexBindingDivisor@8 @412
diff --git a/src/3rdparty/angle/src/libGLESv2/libGLESv2d_mingw32.def b/src/3rdparty/angle/src/libGLESv2/libGLESv2d_mingw32.def
index 2ff4cc0579..a182c21a05 100644
--- a/src/3rdparty/angle/src/libGLESv2/libGLESv2d_mingw32.def
+++ b/src/3rdparty/angle/src/libGLESv2/libGLESv2d_mingw32.def
@@ -1,412 +1,411 @@
LIBRARY libGLESv2
EXPORTS
- glActiveTexture @1
- glAttachShader @2
- glBindAttribLocation @3
- glBindBuffer @4
- glBindFramebuffer @5
- glBindRenderbuffer @6
- glBindTexture @7
- glBlendColor @8
- glBlendEquation @9
- glBlendEquationSeparate @10
- glBlendFunc @11
- glBlendFuncSeparate @12
- glBufferData @13
- glBufferSubData @14
- glCheckFramebufferStatus @15
- glClear @16
- glClearColor @17
- glClearDepthf @18
- glClearStencil @19
- glColorMask @20
- glCompileShader @21
- glCompressedTexImage2D @22
- glCompressedTexSubImage2D @23
- glCopyTexImage2D @24
- glCopyTexSubImage2D @25
- glCreateProgram @26
- glCreateShader @27
- glCullFace @28
- glDeleteBuffers @29
- glDeleteFramebuffers @30
- glDeleteProgram @32
- glDeleteRenderbuffers @33
- glDeleteShader @34
- glDeleteTextures @31
- glDepthFunc @36
- glDepthMask @37
- glDepthRangef @38
- glDetachShader @35
- glDisable @39
- glDisableVertexAttribArray @40
- glDrawArrays @41
- glDrawElements @42
- glEnable @43
- glEnableVertexAttribArray @44
- glFinish @45
- glFlush @46
- glFramebufferRenderbuffer @47
- glFramebufferTexture2D @48
- glFrontFace @49
- glGenBuffers @50
- glGenFramebuffers @52
- glGenRenderbuffers @53
- glGenTextures @54
- glGenerateMipmap @51
- glGetActiveAttrib @55
- glGetActiveUniform @56
- glGetAttachedShaders @57
- glGetAttribLocation @58
- glGetBooleanv @59
- glGetBufferParameteriv @60
- glGetError @61
- glGetFloatv @62
- glGetFramebufferAttachmentParameteriv @63
- glGetIntegerv @64
- glGetProgramInfoLog @66
- glGetProgramiv @65
- glGetRenderbufferParameteriv @67
- glGetShaderInfoLog @69
- glGetShaderPrecisionFormat @70
- glGetShaderSource @71
- glGetShaderiv @68
- glGetString @72
- glGetTexParameterfv @73
- glGetTexParameteriv @74
- glGetUniformLocation @77
- glGetUniformfv @75
- glGetUniformiv @76
- glGetVertexAttribPointerv @80
- glGetVertexAttribfv @78
- glGetVertexAttribiv @79
- glHint @81
- glIsBuffer @82
- glIsEnabled @83
- glIsFramebuffer @84
- glIsProgram @85
- glIsRenderbuffer @86
- glIsShader @87
- glIsTexture @88
- glLineWidth @89
- glLinkProgram @90
- glPixelStorei @91
- glPolygonOffset @92
- glReadPixels @93
- glReleaseShaderCompiler @94
- glRenderbufferStorage @95
- glSampleCoverage @96
- glScissor @97
- glShaderBinary @98
- glShaderSource @99
- glStencilFunc @100
- glStencilFuncSeparate @101
- glStencilMask @102
- glStencilMaskSeparate @103
- glStencilOp @104
- glStencilOpSeparate @105
- glTexImage2D @106
- glTexParameterf @107
- glTexParameterfv @108
- glTexParameteri @109
- glTexParameteriv @110
- glTexSubImage2D @111
- glUniform1f @112
- glUniform1fv @113
- glUniform1i @114
- glUniform1iv @115
- glUniform2f @116
- glUniform2fv @117
- glUniform2i @118
- glUniform2iv @119
- glUniform3f @120
- glUniform3fv @121
- glUniform3i @122
- glUniform3iv @123
- glUniform4f @124
- glUniform4fv @125
- glUniform4i @126
- glUniform4iv @127
- glUniformMatrix2fv @128
- glUniformMatrix3fv @129
- glUniformMatrix4fv @130
- glUseProgram @131
- glValidateProgram @132
- glVertexAttrib1f @133
- glVertexAttrib1fv @134
- glVertexAttrib2f @135
- glVertexAttrib2fv @136
- glVertexAttrib3f @137
- glVertexAttrib3fv @138
- glVertexAttrib4f @139
- glVertexAttrib4fv @140
- glVertexAttribPointer @141
- glViewport @142
+ glActiveTexture@4 @1
+ glAttachShader@8 @2
+ glBindAttribLocation@12 @3
+ glBindBuffer@8 @4
+ glBindFramebuffer@8 @5
+ glBindRenderbuffer@8 @6
+ glBindTexture@8 @7
+ glBlendColor@16 @8
+ glBlendEquation@4 @9
+ glBlendEquationSeparate@8 @10
+ glBlendFunc@8 @11
+ glBlendFuncSeparate@16 @12
+ glBufferData@16 @13
+ glBufferSubData@16 @14
+ glCheckFramebufferStatus@4 @15
+ glClear@4 @16
+ glClearColor@16 @17
+ glClearDepthf@4 @18
+ glClearStencil@4 @19
+ glColorMask@16 @20
+ glCompileShader@4 @21
+ glCompressedTexImage2D@32 @22
+ glCompressedTexSubImage2D@36 @23
+ glCopyTexImage2D@32 @24
+ glCopyTexSubImage2D@32 @25
+ glCreateProgram@0 @26
+ glCreateShader@4 @27
+ glCullFace@4 @28
+ glDeleteBuffers@8 @29
+ glDeleteFramebuffers@8 @30
+ glDeleteProgram@4 @32
+ glDeleteRenderbuffers@8 @33
+ glDeleteShader@4 @34
+ glDeleteTextures@8 @31
+ glDepthFunc@4 @36
+ glDepthMask@4 @37
+ glDepthRangef@8 @38
+ glDetachShader@8 @35
+ glDisable@4 @39
+ glDisableVertexAttribArray@4 @40
+ glDrawArrays@12 @41
+ glDrawElements@16 @42
+ glEnable@4 @43
+ glEnableVertexAttribArray@4 @44
+ glFinish@0 @45
+ glFlush@0 @46
+ glFramebufferRenderbuffer@16 @47
+ glFramebufferTexture2D@20 @48
+ glFrontFace@4 @49
+ glGenBuffers@8 @50
+ glGenFramebuffers@8 @52
+ glGenRenderbuffers@8 @53
+ glGenTextures@8 @54
+ glGenerateMipmap@4 @51
+ glGetActiveAttrib@28 @55
+ glGetActiveUniform@28 @56
+ glGetAttachedShaders@16 @57
+ glGetAttribLocation@8 @58
+ glGetBooleanv@8 @59
+ glGetBufferParameteriv@12 @60
+ glGetError@0 @61
+ glGetFloatv@8 @62
+ glGetFramebufferAttachmentParameteriv@16 @63
+ glGetIntegerv@8 @64
+ glGetProgramInfoLog@16 @66
+ glGetProgramiv@12 @65
+ glGetRenderbufferParameteriv@12 @67
+ glGetShaderInfoLog@16 @69
+ glGetShaderPrecisionFormat@16 @70
+ glGetShaderSource@16 @71
+ glGetShaderiv@12 @68
+ glGetString@4 @72
+ glGetTexParameterfv@12 @73
+ glGetTexParameteriv@12 @74
+ glGetUniformLocation@8 @77
+ glGetUniformfv@12 @75
+ glGetUniformiv@12 @76
+ glGetVertexAttribPointerv@12 @80
+ glGetVertexAttribfv@12 @78
+ glGetVertexAttribiv@12 @79
+ glHint@8 @81
+ glIsBuffer@4 @82
+ glIsEnabled@4 @83
+ glIsFramebuffer@4 @84
+ glIsProgram@4 @85
+ glIsRenderbuffer@4 @86
+ glIsShader@4 @87
+ glIsTexture@4 @88
+ glLineWidth@4 @89
+ glLinkProgram@4 @90
+ glPixelStorei@8 @91
+ glPolygonOffset@8 @92
+ glReadPixels@28 @93
+ glReleaseShaderCompiler@0 @94
+ glRenderbufferStorage@16 @95
+ glSampleCoverage@8 @96
+ glScissor@16 @97
+ glShaderBinary@20 @98
+ glShaderSource@16 @99
+ glStencilFunc@12 @100
+ glStencilFuncSeparate@16 @101
+ glStencilMask@4 @102
+ glStencilMaskSeparate@8 @103
+ glStencilOp@12 @104
+ glStencilOpSeparate@16 @105
+ glTexImage2D@36 @106
+ glTexParameterf@12 @107
+ glTexParameterfv@12 @108
+ glTexParameteri@12 @109
+ glTexParameteriv@12 @110
+ glTexSubImage2D@36 @111
+ glUniform1f@8 @112
+ glUniform1fv@12 @113
+ glUniform1i@8 @114
+ glUniform1iv@12 @115
+ glUniform2f@12 @116
+ glUniform2fv@12 @117
+ glUniform2i@12 @118
+ glUniform2iv@12 @119
+ glUniform3f@16 @120
+ glUniform3fv@12 @121
+ glUniform3i@16 @122
+ glUniform3iv@12 @123
+ glUniform4f@20 @124
+ glUniform4fv@12 @125
+ glUniform4i@20 @126
+ glUniform4iv@12 @127
+ glUniformMatrix2fv@16 @128
+ glUniformMatrix3fv@16 @129
+ glUniformMatrix4fv@16 @130
+ glUseProgram@4 @131
+ glValidateProgram@4 @132
+ glVertexAttrib1f@8 @133
+ glVertexAttrib1fv@8 @134
+ glVertexAttrib2f@12 @135
+ glVertexAttrib2fv@8 @136
+ glVertexAttrib3f@16 @137
+ glVertexAttrib3fv@8 @138
+ glVertexAttrib4f@20 @139
+ glVertexAttrib4fv@8 @140
+ glVertexAttribPointer@24 @141
+ glViewport@16 @142
; Extensions
- glBlitFramebufferANGLE @149
- glRenderbufferStorageMultisampleANGLE @150
- glDeleteFencesNV @151
- glFinishFenceNV @152
- glGenFencesNV @153
- glGetFenceivNV @154
- glIsFenceNV @155
- glSetFenceNV @156
- glTestFenceNV @157
- glGetTranslatedShaderSourceANGLE @159
- glTexStorage2DEXT @160
- glGetGraphicsResetStatusEXT @161
- glReadnPixelsEXT @162
- glGetnUniformfvEXT @163
- glGetnUniformivEXT @164
- glGenQueriesEXT @165
- glDeleteQueriesEXT @166
- glIsQueryEXT @167
- glBeginQueryEXT @168
- glEndQueryEXT @169
- glGetQueryivEXT @170
- glGetQueryObjectuivEXT @171
- glVertexAttribDivisorANGLE @172
- glDrawArraysInstancedANGLE @173
- glDrawElementsInstancedANGLE @174
- glProgramBinaryOES @175
- glGetProgramBinaryOES @176
- glDrawBuffersEXT @179
- glMapBufferOES @285
- glUnmapBufferOES @286
- glGetBufferPointervOES @287
- glMapBufferRangeEXT @288
- glFlushMappedBufferRangeEXT @289
- glDiscardFramebufferEXT @293
- glInsertEventMarkerEXT @294
- glPushGroupMarkerEXT @295
- glPopGroupMarkerEXT @296
- glEGLImageTargetTexture2DOES @297
- glEGLImageTargetRenderbufferStorageOES @298
- glBindVertexArrayOES @299
- glDeleteVertexArraysOES @300
- glGenVertexArraysOES @301
- glIsVertexArrayOES @302
- glDebugMessageControlKHR @303
- glDebugMessageInsertKHR @304
- glDebugMessageCallbackKHR @305
- glGetDebugMessageLogKHR @306
- glPushDebugGroupKHR @307
- glPopDebugGroupKHR @308
- glObjectLabelKHR @309
- glGetObjectLabelKHR @310
- glObjectPtrLabelKHR @311
- glGetObjectPtrLabelKHR @312
- glGetPointervKHR @313
- glQueryCounterEXT @314
- glGetQueryObjectivEXT @315
- glGetQueryObjecti64vEXT @316
- glGetQueryObjectui64vEXT @317
- glBindUniformLocationCHROMIUM @318
- glCoverageModulationCHROMIUM @319
+ glBlitFramebufferANGLE@40 @149
+ glRenderbufferStorageMultisampleANGLE@20 @150
+ glDeleteFencesNV@8 @151
+ glFinishFenceNV@4 @152
+ glGenFencesNV@8 @153
+ glGetFenceivNV@12 @154
+ glIsFenceNV@4 @155
+ glSetFenceNV@8 @156
+ glTestFenceNV@4 @157
+ glGetTranslatedShaderSourceANGLE@16 @159
+ glTexStorage2DEXT@20 @160
+ glGetGraphicsResetStatusEXT@0 @161
+ glReadnPixelsEXT@32 @162
+ glGetnUniformfvEXT@16 @163
+ glGetnUniformivEXT@16 @164
+ glGenQueriesEXT@8 @165
+ glDeleteQueriesEXT@8 @166
+ glIsQueryEXT@4 @167
+ glBeginQueryEXT@8 @168
+ glEndQueryEXT@4 @169
+ glGetQueryivEXT@12 @170
+ glGetQueryObjectuivEXT@12 @171
+ glVertexAttribDivisorANGLE@8 @172
+ glDrawArraysInstancedANGLE@16 @173
+ glDrawElementsInstancedANGLE@20 @174
+ glProgramBinaryOES@16 @175
+ glGetProgramBinaryOES@20 @176
+ glDrawBuffersEXT@8 @179
+ glMapBufferOES@8 @285
+ glUnmapBufferOES@4 @286
+ glGetBufferPointervOES@12 @287
+ glMapBufferRangeEXT@16 @288
+ glFlushMappedBufferRangeEXT@12 @289
+ glDiscardFramebufferEXT@12 @293
+ glInsertEventMarkerEXT@8 @294
+ glPushGroupMarkerEXT@8 @295
+ glPopGroupMarkerEXT@0 @296
+ glEGLImageTargetTexture2DOES@8 @297
+ glEGLImageTargetRenderbufferStorageOES@8 @298
+ glBindVertexArrayOES@4 @299
+ glDeleteVertexArraysOES@8 @300
+ glGenVertexArraysOES@8 @301
+ glIsVertexArrayOES@4 @302
+ glDebugMessageControlKHR@24 @303
+ glDebugMessageInsertKHR@24 @304
+ glDebugMessageCallbackKHR@8 @305
+ glGetDebugMessageLogKHR@32 @306
+ glPushDebugGroupKHR@16 @307
+ glPopDebugGroupKHR@0 @308
+ glObjectLabelKHR@16 @309
+ glGetObjectLabelKHR@20 @310
+ glObjectPtrLabelKHR@12 @311
+ glGetObjectPtrLabelKHR@16 @312
+ glGetPointervKHR@8 @313
+ glQueryCounterEXT@8 @314
+ glGetQueryObjectivEXT@12 @315
+ glGetQueryObjecti64vEXT@12 @316
+ glGetQueryObjectui64vEXT@12 @317
+ glBindUniformLocationCHROMIUM@12 @318
+ glCoverageModulationCHROMIUM@4 @319
+ glMatrixLoadfCHROMIUM@8 @320
+ glMatrixLoadIdentityCHROMIUM@4 @321
+ glGenPathsCHROMIUM@4 @322
+ glDeletePathsCHROMIUM@8 @323
+ glIsPathCHROMIUM@4 @324
+ glPathCommandsCHROMIUM@24 @325
+ glPathParameterfCHROMIUM@12 @326
+ glPathParameteriCHROMIUM@12 @327
+ glGetPathParameterfvCHROMIUM@12 @328
+ glGetPathParameterivCHROMIUM@12 @329
+ glPathStencilFuncCHROMIUM@12 @330
+ glStencilFillPathCHROMIUM@12 @331
+ glStencilStrokePathCHROMIUM@12 @332
+ glCoverFillPathCHROMIUM@8 @333
+ glCoverStrokePathCHROMIUM@8 @334
+ glStencilThenCoverFillPathCHROMIUM@16 @335
+ glStencilThenCoverStrokePathCHROMIUM@16 @336
+ glCoverFillPathInstancedCHROMIUM@28 @337
+ glCoverStrokePathInstancedCHROMIUM@28 @338
+ glStencilStrokePathInstancedCHROMIUM@32 @339
+ glStencilFillPathInstancedCHROMIUM@32 @340
+ glStencilThenCoverFillPathInstancedCHROMIUM@36 @341
+ glStencilThenCoverStrokePathInstancedCHROMIUM@36 @342
+ glBindFragmentInputLocationCHROMIUM@12 @343
+ glProgramPathFragmentInputGenCHROMIUM@20 @344
- glMatrixLoadfCHROMIUM @320
- glMatrixLoadIdentityCHROMIUM @321
- glGenPathsCHROMIUM @322
- glDeletePathsCHROMIUM @323
- glIsPathCHROMIUM @324
- glPathCommandsCHROMIUM @325
- glPathParameterfCHROMIUM @326
- glPathParameteriCHROMIUM @327
- glGetPathParameterfvCHROMIUM @328
- glGetPathParameterivCHROMIUM @329
- glPathStencilFuncCHROMIUM @330
- glStencilFillPathCHROMIUM @331
- glStencilStrokePathCHROMIUM @332
- glCoverFillPathCHROMIUM @333
- glCoverStrokePathCHROMIUM @334
- glStencilThenCoverFillPathCHROMIUM @335
- glStencilThenCoverStrokePathCHROMIUM @336
- glCoverFillPathInstancedCHROMIUM @337
- glCoverStrokePathInstancedCHROMIUM @338
- glStencilStrokePathInstancedCHROMIUM @339
- glStencilFillPathInstancedCHROMIUM @340
- glStencilThenCoverFillPathInstancedCHROMIUM @341
- glStencilThenCoverStrokePathInstancedCHROMIUM @342
- glBindFragmentInputLocationCHROMIUM @343
- glProgramPathFragmentInputGenCHROMIUM @344
-
- glFramebufferTextureMultiviewLayeredANGLE @413
- glFramebufferTextureMultiviewSideBySideANGLE @414
- glRequestExtensionANGLE @415
+ glFramebufferTextureMultiviewLayeredANGLE@24 @413
+ glFramebufferTextureMultiviewSideBySideANGLE@24 @414
+ glRequestExtensionANGLE@4 @415
; GLES 3.0 Functions
- glReadBuffer @180
- glDrawRangeElements @181
- glTexImage3D @182
- glTexSubImage3D @183
- glCopyTexSubImage3D @184
- glCompressedTexImage3D @185
- glCompressedTexSubImage3D @186
- glGenQueries @187
- glDeleteQueries @188
- glIsQuery @189
- glBeginQuery @190
- glEndQuery @191
- glGetQueryiv @192
- glGetQueryObjectuiv @193
- glUnmapBuffer @194
- glGetBufferPointerv @195
- glDrawBuffers @196
- glUniformMatrix2x3fv @197
- glUniformMatrix3x2fv @198
- glUniformMatrix2x4fv @199
- glUniformMatrix4x2fv @200
- glUniformMatrix3x4fv @201
- glUniformMatrix4x3fv @202
- glBlitFramebuffer @203
- glRenderbufferStorageMultisample @204
- glFramebufferTextureLayer @205
- glMapBufferRange @206
- glFlushMappedBufferRange @207
- glBindVertexArray @208
- glDeleteVertexArrays @209
- glGenVertexArrays @210
- glIsVertexArray @211
- glGetIntegeri_v @212
- glBeginTransformFeedback @213
- glEndTransformFeedback @214
- glBindBufferRange @215
- glBindBufferBase @216
- glTransformFeedbackVaryings @217
- glGetTransformFeedbackVarying @218
- glVertexAttribIPointer @219
- glGetVertexAttribIiv @220
- glGetVertexAttribIuiv @221
- glVertexAttribI4i @222
- glVertexAttribI4ui @223
- glVertexAttribI4iv @224
- glVertexAttribI4uiv @225
- glGetUniformuiv @226
- glGetFragDataLocation @227
- glUniform1ui @228
- glUniform2ui @229
- glUniform3ui @230
- glUniform4ui @231
- glUniform1uiv @232
- glUniform2uiv @233
- glUniform3uiv @234
- glUniform4uiv @235
- glClearBufferiv @236
- glClearBufferuiv @237
- glClearBufferfv @238
- glClearBufferfi @239
- glGetStringi @240
- glCopyBufferSubData @241
- glGetUniformIndices @242
- glGetActiveUniformsiv @243
- glGetUniformBlockIndex @244
- glGetActiveUniformBlockiv @245
- glGetActiveUniformBlockName @246
- glUniformBlockBinding @247
- glDrawArraysInstanced @248
- glDrawElementsInstanced @249
- glFenceSync @250
- glIsSync @251
- glDeleteSync @252
- glClientWaitSync @253
- glWaitSync @254
- glGetInteger64v @255
- glGetSynciv @256
- glGetInteger64i_v @257
- glGetBufferParameteri64v @258
- glGenSamplers @259
- glDeleteSamplers @260
- glIsSampler @261
- glBindSampler @262
- glSamplerParameteri @263
- glSamplerParameteriv @264
- glSamplerParameterf @265
- glSamplerParameterfv @266
- glGetSamplerParameteriv @267
- glGetSamplerParameterfv @268
- glVertexAttribDivisor @269
- glBindTransformFeedback @270
- glDeleteTransformFeedbacks @271
- glGenTransformFeedbacks @272
- glIsTransformFeedback @273
- glPauseTransformFeedback @274
- glResumeTransformFeedback @275
- glGetProgramBinary @276
- glProgramBinary @277
- glProgramParameteri @278
- glInvalidateFramebuffer @279
- glInvalidateSubFramebuffer @280
- glTexStorage2D @281
- glTexStorage3D @282
- glGetInternalformativ @283
+ glReadBuffer@4 @180
+ glDrawRangeElements@24 @181
+ glTexImage3D@40 @182
+ glTexSubImage3D@44 @183
+ glCopyTexSubImage3D@36 @184
+ glCompressedTexImage3D@36 @185
+ glCompressedTexSubImage3D@44 @186
+ glGenQueries@8 @187
+ glDeleteQueries@8 @188
+ glIsQuery@4 @189
+ glBeginQuery@8 @190
+ glEndQuery@4 @191
+ glGetQueryiv@12 @192
+ glGetQueryObjectuiv@12 @193
+ glUnmapBuffer@4 @194
+ glGetBufferPointerv@12 @195
+ glDrawBuffers@8 @196
+ glUniformMatrix2x3fv@16 @197
+ glUniformMatrix3x2fv@16 @198
+ glUniformMatrix2x4fv@16 @199
+ glUniformMatrix4x2fv@16 @200
+ glUniformMatrix3x4fv@16 @201
+ glUniformMatrix4x3fv@16 @202
+ glBlitFramebuffer@40 @203
+ glRenderbufferStorageMultisample@20 @204
+ glFramebufferTextureLayer@20 @205
+ glMapBufferRange@16 @206
+ glFlushMappedBufferRange@12 @207
+ glBindVertexArray@4 @208
+ glDeleteVertexArrays@8 @209
+ glGenVertexArrays@8 @210
+ glIsVertexArray@4 @211
+ glGetIntegeri_v@12 @212
+ glBeginTransformFeedback@4 @213
+ glEndTransformFeedback@0 @214
+ glBindBufferRange@20 @215
+ glBindBufferBase@12 @216
+ glTransformFeedbackVaryings@16 @217
+ glGetTransformFeedbackVarying@28 @218
+ glVertexAttribIPointer@20 @219
+ glGetVertexAttribIiv@12 @220
+ glGetVertexAttribIuiv@12 @221
+ glVertexAttribI4i@20 @222
+ glVertexAttribI4ui@20 @223
+ glVertexAttribI4iv@8 @224
+ glVertexAttribI4uiv@8 @225
+ glGetUniformuiv@12 @226
+ glGetFragDataLocation@8 @227
+ glUniform1ui@8 @228
+ glUniform2ui@12 @229
+ glUniform3ui@16 @230
+ glUniform4ui@20 @231
+ glUniform1uiv@12 @232
+ glUniform2uiv@12 @233
+ glUniform3uiv@12 @234
+ glUniform4uiv@12 @235
+ glClearBufferiv@12 @236
+ glClearBufferuiv@12 @237
+ glClearBufferfv@12 @238
+ glClearBufferfi@16 @239
+ glGetStringi@8 @240
+ glCopyBufferSubData@20 @241
+ glGetUniformIndices@16 @242
+ glGetActiveUniformsiv@20 @243
+ glGetUniformBlockIndex@8 @244
+ glGetActiveUniformBlockiv@16 @245
+ glGetActiveUniformBlockName@20 @246
+ glUniformBlockBinding@12 @247
+ glDrawArraysInstanced@16 @248
+ glDrawElementsInstanced@20 @249
+ glFenceSync@8 @250
+ glIsSync@4 @251
+ glDeleteSync@4 @252
+ glClientWaitSync@16 @253
+ glWaitSync@16 @254
+ glGetInteger64v@8 @255
+ glGetSynciv@20 @256
+ glGetInteger64i_v@12 @257
+ glGetBufferParameteri64v@12 @258
+ glGenSamplers@8 @259
+ glDeleteSamplers@8 @260
+ glIsSampler@4 @261
+ glBindSampler@8 @262
+ glSamplerParameteri@12 @263
+ glSamplerParameteriv@12 @264
+ glSamplerParameterf@12 @265
+ glSamplerParameterfv@12 @266
+ glGetSamplerParameteriv@12 @267
+ glGetSamplerParameterfv@12 @268
+ glVertexAttribDivisor@8 @269
+ glBindTransformFeedback@8 @270
+ glDeleteTransformFeedbacks@8 @271
+ glGenTransformFeedbacks@8 @272
+ glIsTransformFeedback@4 @273
+ glPauseTransformFeedback@0 @274
+ glResumeTransformFeedback@0 @275
+ glGetProgramBinary@20 @276
+ glProgramBinary@16 @277
+ glProgramParameteri@12 @278
+ glInvalidateFramebuffer@12 @279
+ glInvalidateSubFramebuffer@28 @280
+ glTexStorage2D@20 @281
+ glTexStorage3D@24 @282
+ glGetInternalformativ@20 @283
; GLES 3.1 Functions
- glDispatchCompute @345
- glDispatchComputeIndirect @346
- glDrawArraysIndirect @347
- glDrawElementsIndirect @348
- glFramebufferParameteri @349
- glGetFramebufferParameteriv @350
- glGetProgramInterfaceiv @351
- glGetProgramResourceIndex @352
- glGetProgramResourceName @353
- glGetProgramResourceiv @354
- glGetProgramResourceLocation @355
- glUseProgramStages @356
- glActiveShaderProgram @357
- glCreateShaderProgramv @358
- glBindProgramPipeline @359
- glDeleteProgramPipelines @360
- glGenProgramPipelines @361
- glIsProgramPipeline @362
- glGetProgramPipelineiv @363
- glProgramUniform1i @364
- glProgramUniform2i @365
- glProgramUniform3i @366
- glProgramUniform4i @367
- glProgramUniform1ui @368
- glProgramUniform2ui @369
- glProgramUniform3ui @370
- glProgramUniform4ui @371
- glProgramUniform1f @372
- glProgramUniform2f @373
- glProgramUniform3f @374
- glProgramUniform4f @375
- glProgramUniform1iv @376
- glProgramUniform2iv @377
- glProgramUniform3iv @378
- glProgramUniform4iv @379
- glProgramUniform1uiv @380
- glProgramUniform2uiv @381
- glProgramUniform3uiv @382
- glProgramUniform4uiv @383
- glProgramUniform1fv @384
- glProgramUniform2fv @385
- glProgramUniform3fv @386
- glProgramUniform4fv @387
- glProgramUniformMatrix2fv @388
- glProgramUniformMatrix3fv @389
- glProgramUniformMatrix4fv @390
- glProgramUniformMatrix2x3fv @391
- glProgramUniformMatrix3x2fv @392
- glProgramUniformMatrix2x4fv @393
- glProgramUniformMatrix4x2fv @394
- glProgramUniformMatrix3x4fv @395
- glProgramUniformMatrix4x3fv @396
- glValidateProgramPipeline @397
- glGetProgramPipelineInfoLog @398
- glBindImageTexture @399
- glGetBooleani_v @400
- glMemoryBarrier @401
- glMemoryBarrierByRegion @402
- glTexStorage2DMultisample @403
- glGetMultisamplefv @404
- glSampleMaski @405
- glGetTexLevelParameteriv @406
- glGetTexLevelParameterfv @407
- glBindVertexBuffer @408
- glVertexAttribFormat @409
- glVertexAttribIFormat @410
- glVertexAttribBinding @411
- glVertexBindingDivisor @412
+ glDispatchCompute@12 @345
+ glDispatchComputeIndirect@4 @346
+ glDrawArraysIndirect@8 @347
+ glDrawElementsIndirect@12 @348
+ glFramebufferParameteri@12 @349
+ glGetFramebufferParameteriv@12 @350
+ glGetProgramInterfaceiv@16 @351
+ glGetProgramResourceIndex@12 @352
+ glGetProgramResourceName@24 @353
+ glGetProgramResourceiv@32 @354
+ glGetProgramResourceLocation@12 @355
+ glUseProgramStages@12 @356
+ glActiveShaderProgram@8 @357
+ glCreateShaderProgramv@12 @358
+ glBindProgramPipeline@4 @359
+ glDeleteProgramPipelines@8 @360
+ glGenProgramPipelines@8 @361
+ glIsProgramPipeline@4 @362
+ glGetProgramPipelineiv@12 @363
+ glProgramUniform1i@12 @364
+ glProgramUniform2i@16 @365
+ glProgramUniform3i@20 @366
+ glProgramUniform4i@24 @367
+ glProgramUniform1ui@12 @368
+ glProgramUniform2ui@16 @369
+ glProgramUniform3ui@20 @370
+ glProgramUniform4ui@24 @371
+ glProgramUniform1f@12 @372
+ glProgramUniform2f@16 @373
+ glProgramUniform3f@20 @374
+ glProgramUniform4f@24 @375
+ glProgramUniform1iv@16 @376
+ glProgramUniform2iv@16 @377
+ glProgramUniform3iv@16 @378
+ glProgramUniform4iv@16 @379
+ glProgramUniform1uiv@16 @380
+ glProgramUniform2uiv@16 @381
+ glProgramUniform3uiv@16 @382
+ glProgramUniform4uiv@16 @383
+ glProgramUniform1fv@16 @384
+ glProgramUniform2fv@16 @385
+ glProgramUniform3fv@16 @386
+ glProgramUniform4fv@16 @387
+ glProgramUniformMatrix2fv@20 @388
+ glProgramUniformMatrix3fv@20 @389
+ glProgramUniformMatrix4fv@20 @390
+ glProgramUniformMatrix2x3fv@20 @391
+ glProgramUniformMatrix3x2fv@20 @392
+ glProgramUniformMatrix2x4fv@20 @393
+ glProgramUniformMatrix4x2fv@20 @394
+ glProgramUniformMatrix3x4fv@20 @395
+ glProgramUniformMatrix4x3fv@20 @396
+ glValidateProgramPipeline@4 @397
+ glGetProgramPipelineInfoLog@16 @398
+ glBindImageTexture@28 @399
+ glGetBooleani_v@12 @400
+ glMemoryBarrier@4 @401
+ glMemoryBarrierByRegion@4 @402
+ glTexStorage2DMultisample@24 @403
+ glGetMultisamplefv@12 @404
+ glSampleMaski@8 @405
+ glGetTexLevelParameteriv@16 @406
+ glGetTexLevelParameterfv@16 @407
+ glBindVertexBuffer@16 @408
+ glVertexAttribFormat@20 @409
+ glVertexAttribIFormat@16 @410
+ glVertexAttribBinding@8 @411
+ glVertexBindingDivisor@8 @412
diff --git a/src/3rdparty/sqlite/qt_attribution.json b/src/3rdparty/sqlite/qt_attribution.json
index 30f266ee23..e6647b2700 100644
--- a/src/3rdparty/sqlite/qt_attribution.json
+++ b/src/3rdparty/sqlite/qt_attribution.json
@@ -6,8 +6,8 @@
"Description": "SQLite is a small C library that implements a self-contained, embeddable, zero-configuration SQL database engine.",
"Homepage": "https://www.sqlite.org/",
- "Version": "3.27.1",
- "DownloadLocation": "https://www.sqlite.org/2018/sqlite-amalgamation-3270100.zip",
+ "Version": "3.28.0",
+ "DownloadLocation": "https://www.sqlite.org/2019/sqlite-amalgamation-3280000.zip",
"License": "Public Domain",
"Copyright": "The authors disclaim copyright to the source code. However, a license can be obtained if needed."
}
diff --git a/src/3rdparty/sqlite/sqlite3.c b/src/3rdparty/sqlite/sqlite3.c
index 70e84b589c..440429527d 100644
--- a/src/3rdparty/sqlite/sqlite3.c
+++ b/src/3rdparty/sqlite/sqlite3.c
@@ -1,6 +1,6 @@
/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
-** version 3.27.1. By combining all the individual C code files into this
+** version 3.28.0. By combining all the individual C code files into this
** single large file, the entire code can be compiled as a single translation
** unit. This allows many compilers to do optimizations that would not be
** possible if the files were compiled separately. Performance improvements
@@ -1162,9 +1162,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.27.1"
-#define SQLITE_VERSION_NUMBER 3027001
-#define SQLITE_SOURCE_ID "2019-02-08 13:17:39 0eca3dd3d38b31c92b49ca2d311128b74584714d9e7de895b1a6286ef959a1dd"
+#define SQLITE_VERSION "3.28.0"
+#define SQLITE_VERSION_NUMBER 3028000
+#define SQLITE_SOURCE_ID "2019-04-16 19:49:53 884b4b7e502b4e991677b53971277adfaf0a04a284f8e483e2553d0f83156b50"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -1228,6 +1228,9 @@ SQLITE_API int sqlite3_libversion_number(void);
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
SQLITE_API int sqlite3_compileoption_used(const char *zOptName);
SQLITE_API const char *sqlite3_compileoption_get(int N);
+#else
+# define sqlite3_compileoption_used(X) 0
+# define sqlite3_compileoption_get(X) ((void*)0)
#endif
/*
@@ -3125,8 +3128,8 @@ struct sqlite3_mem_methods {
**
** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
-** <dd> ^This option is used to enable or disable the two-argument
-** version of the [fts3_tokenizer()] function which is part of the
+** <dd> ^This option is used to enable or disable the
+** [fts3_tokenizer()] function which is part of the
** [FTS3] full-text search engine extension.
** There should be two additional arguments.
** The first argument is an integer which is 0 to disable fts3_tokenizer() or
@@ -3238,6 +3241,17 @@ struct sqlite3_mem_methods {
** <li> Direct writes to [shadow tables].
** </ul>
** </dd>
+**
+** [[SQLITE_DBCONFIG_WRITABLE_SCHEMA]] <dt>SQLITE_DBCONFIG_WRITABLE_SCHEMA</dt>
+** <dd>The SQLITE_DBCONFIG_WRITABLE_SCHEMA option activates or deactivates the
+** "writable_schema" flag. This has the same effect and is logically equivalent
+** to setting [PRAGMA writable_schema=ON] or [PRAGMA writable_schema=OFF].
+** The first argument to this setting is an integer which is 0 to disable
+** the writable_schema, positive to enable writable_schema, or negative to
+** leave the setting unchanged. The second parameter is a pointer to an
+** integer into which is written 0 or 1 to indicate whether the writable_schema
+** is enabled or disabled following this call.
+** </dd>
** </dl>
*/
#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
@@ -3251,7 +3265,8 @@ struct sqlite3_mem_methods {
#define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */
#define SQLITE_DBCONFIG_RESET_DATABASE 1009 /* int int* */
#define SQLITE_DBCONFIG_DEFENSIVE 1010 /* int int* */
-#define SQLITE_DBCONFIG_MAX 1010 /* Largest DBCONFIG */
+#define SQLITE_DBCONFIG_WRITABLE_SCHEMA 1011 /* int int* */
+#define SQLITE_DBCONFIG_MAX 1011 /* Largest DBCONFIG */
/*
** CAPI3REF: Enable Or Disable Extended Result Codes
@@ -3408,7 +3423,7 @@ SQLITE_API int sqlite3_changes(sqlite3*);
** not. ^Changes to a view that are intercepted by INSTEAD OF triggers
** are not counted.
**
-** This the [sqlite3_total_changes(D)] interface only reports the number
+** The [sqlite3_total_changes(D)] interface only reports the number
** of rows that changed due to SQL statement run against database
** connection D. Any changes by other database connections are ignored.
** To detect changes against a database file from other database
@@ -4934,6 +4949,18 @@ SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt);
SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt);
/*
+** CAPI3REF: Query The EXPLAIN Setting For A Prepared Statement
+** METHOD: sqlite3_stmt
+**
+** ^The sqlite3_stmt_isexplain(S) interface returns 1 if the
+** prepared statement S is an EXPLAIN statement, or 2 if the
+** statement S is an EXPLAIN QUERY PLAN.
+** ^The sqlite3_stmt_isexplain(S) interface returns 0 if S is
+** an ordinary statement or a NULL pointer.
+*/
+SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt);
+
+/*
** CAPI3REF: Determine If A Prepared Statement Has Been Reset
** METHOD: sqlite3_stmt
**
@@ -5072,7 +5099,9 @@ typedef struct sqlite3_context sqlite3_context;
** ^The fifth argument to the BLOB and string binding interfaces
** is a destructor used to dispose of the BLOB or
** string after SQLite has finished with it. ^The destructor is called
-** to dispose of the BLOB or string even if the call to bind API fails.
+** to dispose of the BLOB or string even if the call to the bind API fails,
+** except the destructor is not called if the third parameter is a NULL
+** pointer or the fourth parameter is negative.
** ^If the fifth argument is
** the special value [SQLITE_STATIC], then SQLite assumes that the
** information is in static, unmanaged space and does not need to be freed.
@@ -5989,6 +6018,8 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6
** <tr><td><b>sqlite3_value_nochange&nbsp;&nbsp;</b>
** <td>&rarr;&nbsp;&nbsp;<td>True if the column is unchanged in an UPDATE
** against a virtual table.
+** <tr><td><b>sqlite3_value_frombind&nbsp;&nbsp;</b>
+** <td>&rarr;&nbsp;&nbsp;<td>True if value originated from a [bound parameter]
** </table></blockquote>
**
** <b>Details:</b>
@@ -6050,6 +6081,11 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6
** than within an [xUpdate] method call for an UPDATE statement, then
** the return value is arbitrary and meaningless.
**
+** ^The sqlite3_value_frombind(X) interface returns non-zero if the
+** value X originated from one of the [sqlite3_bind_int|sqlite3_bind()]
+** interfaces. ^If X comes from an SQL literal value, or a table column,
+** and expression, then sqlite3_value_frombind(X) returns zero.
+**
** Please pay particular attention to the fact that the pointer returned
** from [sqlite3_value_blob()], [sqlite3_value_text()], or
** [sqlite3_value_text16()] can be invalidated by a subsequent call to
@@ -6095,6 +6131,7 @@ SQLITE_API int sqlite3_value_bytes16(sqlite3_value*);
SQLITE_API int sqlite3_value_type(sqlite3_value*);
SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);
SQLITE_API int sqlite3_value_nochange(sqlite3_value*);
+SQLITE_API int sqlite3_value_frombind(sqlite3_value*);
/*
** CAPI3REF: Finding The Subtype Of SQL Values
@@ -6830,7 +6867,7 @@ SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*);
** associated with database N of connection D. ^The main database file
** has the name "main". If there is no attached database N on the database
** connection D, or if database N is a temporary or in-memory database, then
-** a NULL pointer is returned.
+** this function will return either a NULL pointer or an empty string.
**
** ^The filename returned by this function is the output of the
** xFullPathname method of the [VFS]. ^In other words, the filename
@@ -11931,7 +11968,7 @@ SQLITE_API int sqlite3rebaser_configure(
** in size. This function allocates and populates a buffer with a copy
** of the changeset rebased rebased according to the configuration of the
** rebaser object passed as the first argument. If successful, (*ppOut)
-** is set to point to the new buffer containing the rebased changset and
+** is set to point to the new buffer containing the rebased changeset and
** (*pnOut) to its size in bytes and SQLITE_OK returned. It is the
** responsibility of the caller to eventually free the new buffer using
** sqlite3_free(). Otherwise, if an error occurs, (*ppOut) and (*pnOut)
@@ -12340,7 +12377,7 @@ struct Fts5PhraseIter {
** Save the pointer passed as the second argument as the extension functions
** "auxiliary data". The pointer may then be retrieved by the current or any
** future invocation of the same fts5 extension function made as part of
-** of the same MATCH query using the xGetAuxdata() API.
+** the same MATCH query using the xGetAuxdata() API.
**
** Each extension function is allocated a single auxiliary data slot for
** each FTS query (MATCH expression). If the extension function is invoked
@@ -12355,7 +12392,7 @@ struct Fts5PhraseIter {
** The xDelete callback, if one is specified, is also invoked on the
** auxiliary data pointer after the FTS5 query has finished.
**
-** If an error (e.g. an OOM condition) occurs within this function, an
+** If an error (e.g. an OOM condition) occurs within this function,
** the auxiliary data is set to NULL and an error code returned. If the
** xDelete parameter was not NULL, it is invoked on the auxiliary data
** pointer before returning.
@@ -13381,7 +13418,7 @@ struct Hash {
unsigned int count; /* Number of entries in this table */
HashElem *first; /* The first element of the array */
struct _ht { /* the hash table */
- int count; /* Number of entries with this hash */
+ unsigned int count; /* Number of entries with this hash */
HashElem *chain; /* Pointer to first entry with this hash */
} *ht;
};
@@ -13522,99 +13559,94 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*);
#define TK_PRECEDING 85
#define TK_RANGE 86
#define TK_UNBOUNDED 87
-#define TK_REINDEX 88
-#define TK_RENAME 89
-#define TK_CTIME_KW 90
-#define TK_ANY 91
-#define TK_BITAND 92
-#define TK_BITOR 93
-#define TK_LSHIFT 94
-#define TK_RSHIFT 95
-#define TK_PLUS 96
-#define TK_MINUS 97
-#define TK_STAR 98
-#define TK_SLASH 99
-#define TK_REM 100
-#define TK_CONCAT 101
-#define TK_COLLATE 102
-#define TK_BITNOT 103
-#define TK_ON 104
-#define TK_INDEXED 105
-#define TK_STRING 106
-#define TK_JOIN_KW 107
-#define TK_CONSTRAINT 108
-#define TK_DEFAULT 109
-#define TK_NULL 110
-#define TK_PRIMARY 111
-#define TK_UNIQUE 112
-#define TK_CHECK 113
-#define TK_REFERENCES 114
-#define TK_AUTOINCR 115
-#define TK_INSERT 116
-#define TK_DELETE 117
-#define TK_UPDATE 118
-#define TK_SET 119
-#define TK_DEFERRABLE 120
-#define TK_FOREIGN 121
-#define TK_DROP 122
-#define TK_UNION 123
-#define TK_ALL 124
-#define TK_EXCEPT 125
-#define TK_INTERSECT 126
-#define TK_SELECT 127
-#define TK_VALUES 128
-#define TK_DISTINCT 129
-#define TK_DOT 130
-#define TK_FROM 131
-#define TK_JOIN 132
-#define TK_USING 133
-#define TK_ORDER 134
-#define TK_GROUP 135
-#define TK_HAVING 136
-#define TK_LIMIT 137
-#define TK_WHERE 138
-#define TK_INTO 139
-#define TK_NOTHING 140
-#define TK_FLOAT 141
-#define TK_BLOB 142
-#define TK_INTEGER 143
-#define TK_VARIABLE 144
-#define TK_CASE 145
-#define TK_WHEN 146
-#define TK_THEN 147
-#define TK_ELSE 148
-#define TK_INDEX 149
-#define TK_ALTER 150
-#define TK_ADD 151
-#define TK_WINDOW 152
-#define TK_OVER 153
-#define TK_FILTER 154
-#define TK_TRUEFALSE 155
-#define TK_ISNOT 156
-#define TK_FUNCTION 157
-#define TK_COLUMN 158
-#define TK_AGG_FUNCTION 159
-#define TK_AGG_COLUMN 160
-#define TK_UMINUS 161
-#define TK_UPLUS 162
-#define TK_TRUTH 163
-#define TK_REGISTER 164
-#define TK_VECTOR 165
-#define TK_SELECT_COLUMN 166
-#define TK_IF_NULL_ROW 167
-#define TK_ASTERISK 168
-#define TK_SPAN 169
-#define TK_END_OF_FILE 170
-#define TK_UNCLOSED_STRING 171
-#define TK_SPACE 172
-#define TK_ILLEGAL 173
-
-/* The token codes above must all fit in 8 bits */
-#define TKFLG_MASK 0xff
-
-/* Flags that can be added to a token code when it is not
-** being stored in a u8: */
-#define TKFLG_DONTFOLD 0x100 /* Omit constant folding optimizations */
+#define TK_EXCLUDE 88
+#define TK_GROUPS 89
+#define TK_OTHERS 90
+#define TK_TIES 91
+#define TK_REINDEX 92
+#define TK_RENAME 93
+#define TK_CTIME_KW 94
+#define TK_ANY 95
+#define TK_BITAND 96
+#define TK_BITOR 97
+#define TK_LSHIFT 98
+#define TK_RSHIFT 99
+#define TK_PLUS 100
+#define TK_MINUS 101
+#define TK_STAR 102
+#define TK_SLASH 103
+#define TK_REM 104
+#define TK_CONCAT 105
+#define TK_COLLATE 106
+#define TK_BITNOT 107
+#define TK_ON 108
+#define TK_INDEXED 109
+#define TK_STRING 110
+#define TK_JOIN_KW 111
+#define TK_CONSTRAINT 112
+#define TK_DEFAULT 113
+#define TK_NULL 114
+#define TK_PRIMARY 115
+#define TK_UNIQUE 116
+#define TK_CHECK 117
+#define TK_REFERENCES 118
+#define TK_AUTOINCR 119
+#define TK_INSERT 120
+#define TK_DELETE 121
+#define TK_UPDATE 122
+#define TK_SET 123
+#define TK_DEFERRABLE 124
+#define TK_FOREIGN 125
+#define TK_DROP 126
+#define TK_UNION 127
+#define TK_ALL 128
+#define TK_EXCEPT 129
+#define TK_INTERSECT 130
+#define TK_SELECT 131
+#define TK_VALUES 132
+#define TK_DISTINCT 133
+#define TK_DOT 134
+#define TK_FROM 135
+#define TK_JOIN 136
+#define TK_USING 137
+#define TK_ORDER 138
+#define TK_GROUP 139
+#define TK_HAVING 140
+#define TK_LIMIT 141
+#define TK_WHERE 142
+#define TK_INTO 143
+#define TK_NOTHING 144
+#define TK_FLOAT 145
+#define TK_BLOB 146
+#define TK_INTEGER 147
+#define TK_VARIABLE 148
+#define TK_CASE 149
+#define TK_WHEN 150
+#define TK_THEN 151
+#define TK_ELSE 152
+#define TK_INDEX 153
+#define TK_ALTER 154
+#define TK_ADD 155
+#define TK_WINDOW 156
+#define TK_OVER 157
+#define TK_FILTER 158
+#define TK_TRUEFALSE 159
+#define TK_ISNOT 160
+#define TK_FUNCTION 161
+#define TK_COLUMN 162
+#define TK_AGG_FUNCTION 163
+#define TK_AGG_COLUMN 164
+#define TK_UMINUS 165
+#define TK_UPLUS 166
+#define TK_TRUTH 167
+#define TK_REGISTER 168
+#define TK_VECTOR 169
+#define TK_SELECT_COLUMN 170
+#define TK_IF_NULL_ROW 171
+#define TK_ASTERISK 172
+#define TK_SPAN 173
+#define TK_SPACE 174
+#define TK_ILLEGAL 175
/************** End of parse.h ***********************************************/
/************** Continuing where we left off in sqliteInt.h ******************/
@@ -14546,9 +14578,6 @@ struct BtreePayload {
SQLITE_PRIVATE int sqlite3BtreeInsert(BtCursor*, const BtreePayload *pPayload,
int flags, int seekResult);
SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor*, int *pRes);
-#ifndef SQLITE_OMIT_WINDOWFUNC
-SQLITE_PRIVATE void sqlite3BtreeSkipNext(BtCursor*);
-#endif
SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor*, int *pRes);
SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor*, int flags);
SQLITE_PRIVATE int sqlite3BtreeEof(BtCursor*);
@@ -14906,25 +14935,25 @@ typedef struct VdbeOpList VdbeOpList;
#define OP_Offset 89 /* synopsis: r[P3] = sqlite_offset(P1) */
#define OP_Column 90 /* synopsis: r[P3]=PX */
#define OP_Affinity 91 /* synopsis: affinity(r[P1@P2]) */
-#define OP_BitAnd 92 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */
-#define OP_BitOr 93 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */
-#define OP_ShiftLeft 94 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<<r[P1] */
-#define OP_ShiftRight 95 /* same as TK_RSHIFT, synopsis: r[P3]=r[P2]>>r[P1] */
-#define OP_Add 96 /* same as TK_PLUS, synopsis: r[P3]=r[P1]+r[P2] */
-#define OP_Subtract 97 /* same as TK_MINUS, synopsis: r[P3]=r[P2]-r[P1] */
-#define OP_Multiply 98 /* same as TK_STAR, synopsis: r[P3]=r[P1]*r[P2] */
-#define OP_Divide 99 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */
-#define OP_Remainder 100 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */
-#define OP_Concat 101 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */
-#define OP_MakeRecord 102 /* synopsis: r[P3]=mkrec(r[P1@P2]) */
-#define OP_BitNot 103 /* same as TK_BITNOT, synopsis: r[P2]= ~r[P1] */
-#define OP_Count 104 /* synopsis: r[P2]=count() */
-#define OP_ReadCookie 105
-#define OP_String8 106 /* same as TK_STRING, synopsis: r[P2]='P4' */
-#define OP_SetCookie 107
-#define OP_ReopenIdx 108 /* synopsis: root=P2 iDb=P3 */
-#define OP_OpenRead 109 /* synopsis: root=P2 iDb=P3 */
-#define OP_OpenWrite 110 /* synopsis: root=P2 iDb=P3 */
+#define OP_MakeRecord 92 /* synopsis: r[P3]=mkrec(r[P1@P2]) */
+#define OP_Count 93 /* synopsis: r[P2]=count() */
+#define OP_ReadCookie 94
+#define OP_SetCookie 95
+#define OP_BitAnd 96 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */
+#define OP_BitOr 97 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */
+#define OP_ShiftLeft 98 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<<r[P1] */
+#define OP_ShiftRight 99 /* same as TK_RSHIFT, synopsis: r[P3]=r[P2]>>r[P1] */
+#define OP_Add 100 /* same as TK_PLUS, synopsis: r[P3]=r[P1]+r[P2] */
+#define OP_Subtract 101 /* same as TK_MINUS, synopsis: r[P3]=r[P2]-r[P1] */
+#define OP_Multiply 102 /* same as TK_STAR, synopsis: r[P3]=r[P1]*r[P2] */
+#define OP_Divide 103 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */
+#define OP_Remainder 104 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */
+#define OP_Concat 105 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */
+#define OP_ReopenIdx 106 /* synopsis: root=P2 iDb=P3 */
+#define OP_BitNot 107 /* same as TK_BITNOT, synopsis: r[P2]= ~r[P1] */
+#define OP_OpenRead 108 /* synopsis: root=P2 iDb=P3 */
+#define OP_OpenWrite 109 /* synopsis: root=P2 iDb=P3 */
+#define OP_String8 110 /* same as TK_STRING, synopsis: r[P2]='P4' */
#define OP_OpenDup 111
#define OP_OpenAutoindex 112 /* synopsis: nColumn=P2 */
#define OP_OpenEphemeral 113 /* synopsis: nColumn=P2 */
@@ -14937,57 +14966,56 @@ typedef struct VdbeOpList VdbeOpList;
#define OP_Sequence 120 /* synopsis: r[P2]=cursor[P1].ctr++ */
#define OP_NewRowid 121 /* synopsis: r[P2]=rowid */
#define OP_Insert 122 /* synopsis: intkey=r[P3] data=r[P2] */
-#define OP_InsertInt 123 /* synopsis: intkey=P3 data=r[P2] */
-#define OP_Delete 124
-#define OP_ResetCount 125
-#define OP_SorterCompare 126 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */
-#define OP_SorterData 127 /* synopsis: r[P2]=data */
-#define OP_RowData 128 /* synopsis: r[P2]=data */
-#define OP_Rowid 129 /* synopsis: r[P2]=rowid */
-#define OP_NullRow 130
-#define OP_SeekEnd 131
-#define OP_SorterInsert 132 /* synopsis: key=r[P2] */
-#define OP_IdxInsert 133 /* synopsis: key=r[P2] */
-#define OP_IdxDelete 134 /* synopsis: key=r[P2@P3] */
-#define OP_DeferredSeek 135 /* synopsis: Move P3 to P1.rowid if needed */
-#define OP_IdxRowid 136 /* synopsis: r[P2]=rowid */
-#define OP_Destroy 137
-#define OP_Clear 138
-#define OP_ResetSorter 139
-#define OP_CreateBtree 140 /* synopsis: r[P2]=root iDb=P1 flags=P3 */
-#define OP_Real 141 /* same as TK_FLOAT, synopsis: r[P2]=P4 */
-#define OP_SqlExec 142
-#define OP_ParseSchema 143
-#define OP_LoadAnalysis 144
-#define OP_DropTable 145
-#define OP_DropIndex 146
-#define OP_DropTrigger 147
-#define OP_IntegrityCk 148
-#define OP_RowSetAdd 149 /* synopsis: rowset(P1)=r[P2] */
-#define OP_Param 150
-#define OP_FkCounter 151 /* synopsis: fkctr[P1]+=P2 */
-#define OP_MemMax 152 /* synopsis: r[P1]=max(r[P1],r[P2]) */
-#define OP_OffsetLimit 153 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */
-#define OP_AggInverse 154 /* synopsis: accum=r[P3] inverse(r[P2@P5]) */
-#define OP_AggStep 155 /* synopsis: accum=r[P3] step(r[P2@P5]) */
-#define OP_AggStep1 156 /* synopsis: accum=r[P3] step(r[P2@P5]) */
-#define OP_AggValue 157 /* synopsis: r[P3]=value N=P2 */
-#define OP_AggFinal 158 /* synopsis: accum=r[P1] N=P2 */
-#define OP_Expire 159
-#define OP_TableLock 160 /* synopsis: iDb=P1 root=P2 write=P3 */
-#define OP_VBegin 161
-#define OP_VCreate 162
-#define OP_VDestroy 163
-#define OP_VOpen 164
-#define OP_VColumn 165 /* synopsis: r[P3]=vcolumn(P2) */
-#define OP_VRename 166
-#define OP_Pagecount 167
-#define OP_MaxPgcnt 168
-#define OP_Trace 169
-#define OP_CursorHint 170
-#define OP_Noop 171
-#define OP_Explain 172
-#define OP_Abortable 173
+#define OP_Delete 123
+#define OP_ResetCount 124
+#define OP_SorterCompare 125 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */
+#define OP_SorterData 126 /* synopsis: r[P2]=data */
+#define OP_RowData 127 /* synopsis: r[P2]=data */
+#define OP_Rowid 128 /* synopsis: r[P2]=rowid */
+#define OP_NullRow 129
+#define OP_SeekEnd 130
+#define OP_SorterInsert 131 /* synopsis: key=r[P2] */
+#define OP_IdxInsert 132 /* synopsis: key=r[P2] */
+#define OP_IdxDelete 133 /* synopsis: key=r[P2@P3] */
+#define OP_DeferredSeek 134 /* synopsis: Move P3 to P1.rowid if needed */
+#define OP_IdxRowid 135 /* synopsis: r[P2]=rowid */
+#define OP_Destroy 136
+#define OP_Clear 137
+#define OP_ResetSorter 138
+#define OP_CreateBtree 139 /* synopsis: r[P2]=root iDb=P1 flags=P3 */
+#define OP_SqlExec 140
+#define OP_ParseSchema 141
+#define OP_LoadAnalysis 142
+#define OP_DropTable 143
+#define OP_DropIndex 144
+#define OP_Real 145 /* same as TK_FLOAT, synopsis: r[P2]=P4 */
+#define OP_DropTrigger 146
+#define OP_IntegrityCk 147
+#define OP_RowSetAdd 148 /* synopsis: rowset(P1)=r[P2] */
+#define OP_Param 149
+#define OP_FkCounter 150 /* synopsis: fkctr[P1]+=P2 */
+#define OP_MemMax 151 /* synopsis: r[P1]=max(r[P1],r[P2]) */
+#define OP_OffsetLimit 152 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */
+#define OP_AggInverse 153 /* synopsis: accum=r[P3] inverse(r[P2@P5]) */
+#define OP_AggStep 154 /* synopsis: accum=r[P3] step(r[P2@P5]) */
+#define OP_AggStep1 155 /* synopsis: accum=r[P3] step(r[P2@P5]) */
+#define OP_AggValue 156 /* synopsis: r[P3]=value N=P2 */
+#define OP_AggFinal 157 /* synopsis: accum=r[P1] N=P2 */
+#define OP_Expire 158
+#define OP_TableLock 159 /* synopsis: iDb=P1 root=P2 write=P3 */
+#define OP_VBegin 160
+#define OP_VCreate 161
+#define OP_VDestroy 162
+#define OP_VOpen 163
+#define OP_VColumn 164 /* synopsis: r[P3]=vcolumn(P2) */
+#define OP_VRename 165
+#define OP_Pagecount 166
+#define OP_MaxPgcnt 167
+#define OP_Trace 168
+#define OP_CursorHint 169
+#define OP_Noop 170
+#define OP_Explain 171
+#define OP_Abortable 172
/* Properties such as "out2" or "jump" that are specified in
** comments following the "case" for each opcode in the vdbe.c
@@ -15011,17 +15039,17 @@ typedef struct VdbeOpList VdbeOpList;
/* 64 */ 0x00, 0x00, 0x02, 0x02, 0x08, 0x00, 0x10, 0x10,\
/* 72 */ 0x10, 0x10, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10,\
/* 80 */ 0x10, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00,\
-/* 88 */ 0x12, 0x20, 0x00, 0x00, 0x26, 0x26, 0x26, 0x26,\
-/* 96 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x00, 0x12,\
-/* 104 */ 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,\
+/* 88 */ 0x12, 0x20, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00,\
+/* 96 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\
+/* 104 */ 0x26, 0x26, 0x00, 0x12, 0x00, 0x00, 0x10, 0x00,\
/* 112 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
/* 120 */ 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
-/* 128 */ 0x00, 0x10, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00,\
-/* 136 */ 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00,\
-/* 144 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, 0x00,\
-/* 152 */ 0x04, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
-/* 160 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,\
-/* 168 */ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,}
+/* 128 */ 0x10, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x10,\
+/* 136 */ 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,\
+/* 144 */ 0x00, 0x10, 0x00, 0x00, 0x06, 0x10, 0x00, 0x04,\
+/* 152 */ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+/* 160 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10,\
+/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00,}
/* The sqlite3P2Values() routine is able to run faster if it knows
** the value of the largest JUMP opcode. The smaller the maximum
@@ -16326,6 +16354,7 @@ struct sqlite3 {
void (*xRollbackCallback)(void*); /* Invoked at every commit. */
void *pUpdateArg;
void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64);
+ Parse *pParse; /* Current parse */
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
void *pPreUpdateArg; /* First argument to xPreUpdateCallback */
void (*xPreUpdateCallback)( /* Registered using sqlite3_preupdate_hook() */
@@ -16459,7 +16488,8 @@ struct sqlite3 {
#define DBFLAG_SchemaChange 0x0001 /* Uncommitted Hash table changes */
#define DBFLAG_PreferBuiltin 0x0002 /* Preference to built-in funcs */
#define DBFLAG_Vacuum 0x0004 /* Currently in a VACUUM */
-#define DBFLAG_SchemaKnownOk 0x0008 /* Schema is known to be valid */
+#define DBFLAG_VacuumInto 0x0008 /* Currently running VACUUM INTO */
+#define DBFLAG_SchemaKnownOk 0x0010 /* Schema is known to be valid */
/*
** Bits of the sqlite3.dbOptFlags field that are used by the
@@ -16467,7 +16497,7 @@ struct sqlite3 {
** selectively disable various optimizations.
*/
#define SQLITE_QueryFlattener 0x0001 /* Query flattening */
- /* 0x0002 available for reuse */
+#define SQLITE_WindowFunc 0x0002 /* Use xInverse for window functions */
#define SQLITE_GroupByOrder 0x0004 /* GROUPBY cover of ORDERBY */
#define SQLITE_FactorOutConst 0x0008 /* Constant factoring */
#define SQLITE_DistinctOpt 0x0010 /* DISTINCT using indexes */
@@ -16585,7 +16615,6 @@ struct FuncDestructor {
#define SQLITE_FUNC_AFFINITY 0x4000 /* Built-in affinity() function */
#define SQLITE_FUNC_OFFSET 0x8000 /* Built-in sqlite_offset() function */
#define SQLITE_FUNC_WINDOW 0x00010000 /* Built-in window-only function */
-#define SQLITE_FUNC_WINDOW_SIZE 0x20000 /* Requires partition size as arg. */
#define SQLITE_FUNC_INTERNAL 0x00040000 /* For use by NestedParse() only */
/*
@@ -17391,12 +17420,16 @@ struct Expr {
/*
** The following are the meanings of bits in the Expr.flags field.
+** Value restrictions:
+**
+** EP_Agg == NC_HasAgg == SF_HasAgg
+** EP_Win == NC_HasWin
*/
#define EP_FromJoin 0x000001 /* Originates in ON/USING clause of outer join */
-#define EP_Agg 0x000002 /* Contains one or more aggregate functions */
+#define EP_Distinct 0x000002 /* Aggregate function with DISTINCT keyword */
#define EP_HasFunc 0x000004 /* Contains one or more functions of any kind */
#define EP_FixedCol 0x000008 /* TK_Column with a known fixed value */
-#define EP_Distinct 0x000010 /* Aggregate function with DISTINCT keyword */
+#define EP_Agg 0x000010 /* Contains one or more aggregate functions */
#define EP_VarSelect 0x000020 /* pSelect is correlated, not constant */
#define EP_DblQuoted 0x000040 /* token.z was originally in "..." */
#define EP_InfixFunc 0x000080 /* True for an infix function: LIKE, GLOB, etc */
@@ -17407,7 +17440,7 @@ struct Expr {
#define EP_Skip 0x001000 /* COLLATE, AS, or UNLIKELY */
#define EP_Reduced 0x002000 /* Expr struct EXPR_REDUCEDSIZE bytes only */
#define EP_TokenOnly 0x004000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */
-#define EP_Static 0x008000 /* Held in memory not obtained from malloc() */
+#define EP_Win 0x008000 /* Contains window functions */
#define EP_MemToken 0x010000 /* Need to sqlite3DbFree() Expr.zToken */
#define EP_NoReduce 0x020000 /* Cannot EXPRDUP_REDUCE this Expr */
#define EP_Unlikely 0x040000 /* unlikely() or likelihood() function */
@@ -17419,6 +17452,7 @@ struct Expr {
#define EP_WinFunc 0x1000000 /* TK_FUNCTION with Expr.y.pWin set */
#define EP_Subrtn 0x2000000 /* Uses Expr.y.sub. TK_IN, _SELECT, or _EXISTS */
#define EP_Quoted 0x4000000 /* TK_ID was originally quoted */
+#define EP_Static 0x8000000 /* Held in memory not obtained from malloc() */
/*
** The EP_Propagate mask is a set of properties that automatically propagate
@@ -17658,8 +17692,9 @@ struct NameContext {
** Allowed values for the NameContext, ncFlags field.
**
** Value constraints (all checked via assert()):
-** NC_HasAgg == SF_HasAgg
+** NC_HasAgg == SF_HasAgg == EP_Agg
** NC_MinMaxAgg == SF_MinMaxAgg == SQLITE_FUNC_MINMAX
+** NC_HasWin == EP_Win
**
*/
#define NC_AllowAgg 0x0001 /* Aggregate functions are allowed here */
@@ -17675,6 +17710,7 @@ struct NameContext {
#define NC_MinMaxAgg 0x1000 /* min/max aggregates seen. See note above */
#define NC_Complex 0x2000 /* True if a function or subquery seen */
#define NC_AllowWin 0x4000 /* Window functions are allowed here */
+#define NC_HasWin 0x8000 /* One or more window functions seen */
/*
** An instance of the following object describes a single ON CONFLICT
@@ -17989,6 +18025,7 @@ struct Parse {
AutoincInfo *pAinc; /* Information about AUTOINCREMENT counters */
Parse *pToplevel; /* Parse structure for main program (or NULL) */
Table *pTriggerTab; /* Table triggers are being coded for */
+ Parse *pParentParse; /* Parent parser if this parser is nested */
int addrCrTab; /* Address of OP_CreateBtree opcode on CREATE TABLE */
u32 nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */
u32 oldmask; /* Mask of old.* columns referenced */
@@ -18429,7 +18466,7 @@ struct TreeView {
#endif /* SQLITE_DEBUG */
/*
-** This object is used in varioius ways, all related to window functions
+** This object is used in various ways, all related to window functions
**
** (1) A single instance of this structure is attached to the
** the Expr.pWin field for each window function in an expression tree.
@@ -18444,15 +18481,18 @@ struct TreeView {
** object on a linked list attached to Select.pWinDefn.
**
** The uses (1) and (2) are really the same Window object that just happens
-** to be accessible in two different ways. Use (3) is are separate objects.
+** to be accessible in two different ways. Use case (3) are separate objects.
*/
struct Window {
char *zName; /* Name of window (may be NULL) */
+ char *zBase; /* Name of base window for chaining (may be NULL) */
ExprList *pPartition; /* PARTITION BY clause */
ExprList *pOrderBy; /* ORDER BY clause */
- u8 eType; /* TK_RANGE or TK_ROWS */
+ u8 eFrmType; /* TK_RANGE, TK_GROUPS, TK_ROWS, or 0 */
u8 eStart; /* UNBOUNDED, CURRENT, PRECEDING or FOLLOWING */
u8 eEnd; /* UNBOUNDED, CURRENT, PRECEDING or FOLLOWING */
+ u8 bImplicitFrame; /* True if frame was implicitly specified */
+ u8 eExclude; /* TK_NO, TK_CURRENT, TK_TIES, TK_GROUP, or 0 */
Expr *pStart; /* Expression for "<expr> PRECEDING" */
Expr *pEnd; /* Expression for "<expr> FOLLOWING" */
Window *pNextWin; /* Next window function belonging to this SELECT */
@@ -18463,17 +18503,19 @@ struct Window {
int regResult;
int csrApp; /* Function cursor (used by min/max) */
int regApp; /* Function register (also used by min/max) */
- int regPart; /* First in a set of registers holding PARTITION BY
- ** and ORDER BY values for the window */
+ int regPart; /* Array of registers for PARTITION BY values */
Expr *pOwner; /* Expression object this window is attached to */
int nBufferCol; /* Number of columns in buffer table */
int iArgCol; /* Offset of first argument for this function */
+ int regOne; /* Register containing constant value 1 */
+ int regStartRowid;
+ int regEndRowid;
};
#ifndef SQLITE_OMIT_WINDOWFUNC
SQLITE_PRIVATE void sqlite3WindowDelete(sqlite3*, Window*);
SQLITE_PRIVATE void sqlite3WindowListDelete(sqlite3 *db, Window *p);
-SQLITE_PRIVATE Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*);
+SQLITE_PRIVATE Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*, u8);
SQLITE_PRIVATE void sqlite3WindowAttach(Parse*, Expr*, Window*);
SQLITE_PRIVATE int sqlite3WindowCompare(Parse*, Window*, Window*);
SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse*, Window*);
@@ -18484,6 +18526,8 @@ SQLITE_PRIVATE void sqlite3WindowUpdate(Parse*, Window*, Window*, FuncDef*);
SQLITE_PRIVATE Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p);
SQLITE_PRIVATE Window *sqlite3WindowListDup(sqlite3 *db, Window *p);
SQLITE_PRIVATE void sqlite3WindowFunctions(void);
+SQLITE_PRIVATE void sqlite3WindowChain(Parse*, Window*, Window*);
+SQLITE_PRIVATE Window *sqlite3WindowAssemble(Parse*, Window*, ExprList*, ExprList*, Token*);
#else
# define sqlite3WindowDelete(a,b)
# define sqlite3WindowFunctions()
@@ -18713,6 +18757,7 @@ SQLITE_PRIVATE void sqlite3TreeViewWinFunc(TreeView*, const Window*, u8);
SQLITE_PRIVATE void sqlite3SetString(char **, sqlite3*, const char*);
SQLITE_PRIVATE void sqlite3ErrorMsg(Parse*, const char*, ...);
+SQLITE_PRIVATE int sqlite3ErrorToParser(sqlite3*,int);
SQLITE_PRIVATE void sqlite3Dequote(char*);
SQLITE_PRIVATE void sqlite3DequoteExpr(Expr*);
SQLITE_PRIVATE void sqlite3TokenInit(Token*,char*);
@@ -19174,7 +19219,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameColumn(Parse*, SrcList*, Token*, Token*);
SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *, int *);
SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...);
SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*, int);
-SQLITE_PRIVATE void sqlite3CodeRhsOfIN(Parse*, Expr*, int, int);
+SQLITE_PRIVATE void sqlite3CodeRhsOfIN(Parse*, Expr*, int);
SQLITE_PRIVATE int sqlite3CodeSubselect(Parse*, Expr*);
SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*);
SQLITE_PRIVATE void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p);
@@ -20136,11 +20181,11 @@ struct sqlite3_value {
#define MEM_Real 0x0008 /* Value is a real number */
#define MEM_Blob 0x0010 /* Value is a BLOB */
#define MEM_AffMask 0x001f /* Mask of affinity bits */
-/* Available 0x0020 */
+#define MEM_FromBind 0x0020 /* Value originates from sqlite3_bind() */
/* Available 0x0040 */
#define MEM_Undefined 0x0080 /* Value is undefined */
#define MEM_Cleared 0x0100 /* NULL set by OP_Null, not from data */
-#define MEM_TypeMask 0xc1ff /* Mask of type bits */
+#define MEM_TypeMask 0xc1df /* Mask of type bits */
/* Whenever Mem contains a valid string or blob representation, one of
@@ -20173,6 +20218,12 @@ struct sqlite3_value {
((p)->flags = ((p)->flags&~(MEM_TypeMask|MEM_Zero))|f)
/*
+** True if Mem X is a NULL-nochng type.
+*/
+#define MemNullNochng(X) \
+ ((X)->flags==(MEM_Null|MEM_Zero) && (X)->n==0 && (X)->u.nZero==0)
+
+/*
** Return true if a memory cell is not marked as invalid. This macro
** is for use inside assert() statements only.
*/
@@ -27121,6 +27172,9 @@ SQLITE_PRIVATE void sqlite3OomFault(sqlite3 *db){
db->u1.isInterrupted = 1;
}
db->lookaside.bDisable++;
+ if( db->pParse ){
+ db->pParse->rc = SQLITE_NOMEM_BKPT;
+ }
}
}
@@ -27314,7 +27368,8 @@ static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
static void setStrAccumError(StrAccum *p, u8 eError){
assert( eError==SQLITE_NOMEM || eError==SQLITE_TOOBIG );
p->accError = eError;
- p->nAlloc = 0;
+ if( p->mxAlloc ) sqlite3_str_reset(p);
+ if( eError==SQLITE_TOOBIG ) sqlite3ErrorToParser(p->db, eError);
}
/*
@@ -27344,6 +27399,7 @@ static char *getTextArg(PrintfArguments *p){
*/
static char *printfTempBuf(sqlite3_str *pAccum, sqlite3_int64 n){
char *z;
+ if( pAccum->accError ) return 0;
if( n>pAccum->nAlloc && n>pAccum->mxAlloc ){
setStrAccumError(pAccum, SQLITE_TOOBIG);
return 0;
@@ -28063,9 +28119,8 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
return 0;
}
if( p->mxAlloc==0 ){
- N = p->nAlloc - p->nChar - 1;
setStrAccumError(p, SQLITE_TOOBIG);
- return N;
+ return p->nAlloc - p->nChar - 1;
}else{
char *zOld = isMalloced(p) ? p->zText : 0;
i64 szNew = p->nChar;
@@ -28137,7 +28192,7 @@ SQLITE_API void sqlite3_str_append(sqlite3_str *p, const char *z, int N){
assert( z!=0 || N==0 );
assert( p->zText!=0 || p->nChar==0 || p->accError );
assert( N>=0 );
- assert( p->accError==0 || p->nAlloc==0 );
+ assert( p->accError==0 || p->nAlloc==0 || p->mxAlloc==0 );
if( p->nChar+N >= p->nAlloc ){
enlargeAndAppend(p,z,N);
}else if( N ){
@@ -28770,24 +28825,62 @@ SQLITE_PRIVATE void sqlite3TreeViewBound(
** Generate a human-readable explanation for a Window object
*/
SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u8 more){
+ int nElement = 0;
+ if( pWin->pFilter ){
+ sqlite3TreeViewItem(pView, "FILTER", 1);
+ sqlite3TreeViewExpr(pView, pWin->pFilter, 0);
+ sqlite3TreeViewPop(pView);
+ }
pView = sqlite3TreeViewPush(pView, more);
if( pWin->zName ){
- sqlite3TreeViewLine(pView, "OVER %s", pWin->zName);
+ sqlite3TreeViewLine(pView, "OVER %s (%p)", pWin->zName, pWin);
}else{
- sqlite3TreeViewLine(pView, "OVER");
+ sqlite3TreeViewLine(pView, "OVER (%p)", pWin);
+ }
+ if( pWin->zBase ) nElement++;
+ if( pWin->pOrderBy ) nElement++;
+ if( pWin->eFrmType ) nElement++;
+ if( pWin->eExclude ) nElement++;
+ if( pWin->zBase ){
+ sqlite3TreeViewPush(pView, (--nElement)>0);
+ sqlite3TreeViewLine(pView, "window: %s", pWin->zBase);
+ sqlite3TreeViewPop(pView);
}
if( pWin->pPartition ){
- sqlite3TreeViewExprList(pView, pWin->pPartition, 1, "PARTITION-BY");
+ sqlite3TreeViewExprList(pView, pWin->pPartition, nElement>0,"PARTITION-BY");
}
if( pWin->pOrderBy ){
- sqlite3TreeViewExprList(pView, pWin->pOrderBy, 1, "ORDER-BY");
- }
- if( pWin->eType ){
- sqlite3TreeViewItem(pView, pWin->eType==TK_RANGE ? "RANGE" : "ROWS", 0);
+ sqlite3TreeViewExprList(pView, pWin->pOrderBy, (--nElement)>0, "ORDER-BY");
+ }
+ if( pWin->eFrmType ){
+ char zBuf[30];
+ const char *zFrmType = "ROWS";
+ if( pWin->eFrmType==TK_RANGE ) zFrmType = "RANGE";
+ if( pWin->eFrmType==TK_GROUPS ) zFrmType = "GROUPS";
+ sqlite3_snprintf(sizeof(zBuf),zBuf,"%s%s",zFrmType,
+ pWin->bImplicitFrame ? " (implied)" : "");
+ sqlite3TreeViewItem(pView, zBuf, (--nElement)>0);
sqlite3TreeViewBound(pView, pWin->eStart, pWin->pStart, 1);
sqlite3TreeViewBound(pView, pWin->eEnd, pWin->pEnd, 0);
sqlite3TreeViewPop(pView);
}
+ if( pWin->eExclude ){
+ char zBuf[30];
+ const char *zExclude;
+ switch( pWin->eExclude ){
+ case TK_NO: zExclude = "NO OTHERS"; break;
+ case TK_CURRENT: zExclude = "CURRENT ROW"; break;
+ case TK_GROUP: zExclude = "GROUP"; break;
+ case TK_TIES: zExclude = "TIES"; break;
+ default:
+ sqlite3_snprintf(sizeof(zBuf),zBuf,"invalid(%d)", pWin->eExclude);
+ zExclude = zBuf;
+ break;
+ }
+ sqlite3TreeViewPush(pView, 0);
+ sqlite3TreeViewLine(pView, "EXCLUDE %s", zExclude);
+ sqlite3TreeViewPop(pView);
+ }
sqlite3TreeViewPop(pView);
}
#endif /* SQLITE_OMIT_WINDOWFUNC */
@@ -29767,11 +29860,11 @@ SQLITE_PRIVATE u32 sqlite3Utf8Read(
** encoding, or if *pMem does not contain a string value.
*/
SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){
- int len; /* Maximum length of output string in bytes */
- unsigned char *zOut; /* Output buffer */
- unsigned char *zIn; /* Input iterator */
- unsigned char *zTerm; /* End of input */
- unsigned char *z; /* Output iterator */
+ sqlite3_int64 len; /* Maximum length of output string in bytes */
+ unsigned char *zOut; /* Output buffer */
+ unsigned char *zIn; /* Input iterator */
+ unsigned char *zTerm; /* End of input */
+ unsigned char *z; /* Output iterator */
unsigned int c;
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
@@ -29820,14 +29913,14 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desired
** nul-terminator.
*/
pMem->n &= ~1;
- len = pMem->n * 2 + 1;
+ len = 2 * (sqlite3_int64)pMem->n + 1;
}else{
/* When converting from UTF-8 to UTF-16 the maximum growth is caused
** when a 1-byte UTF-8 character is translated into a 2-byte UTF-16
** character. Two bytes are required in the output buffer for the
** nul-terminator.
*/
- len = pMem->n * 2 + 2;
+ len = 2 * (sqlite3_int64)pMem->n + 2;
}
/* Set zIn to point at the start of the input buffer and zTerm to point 1
@@ -30134,15 +30227,23 @@ SQLITE_PRIVATE void sqlite3Coverage(int x){
#endif
/*
-** Give a callback to the test harness that can be used to simulate faults
-** in places where it is difficult or expensive to do so purely by means
-** of inputs.
+** Calls to sqlite3FaultSim() are used to simulate a failure during testing,
+** or to bypass normal error detection during testing in order to let
+** execute proceed futher downstream.
**
-** The intent of the integer argument is to let the fault simulator know
-** which of multiple sqlite3FaultSim() calls has been hit.
+** In deployment, sqlite3FaultSim() *always* return SQLITE_OK (0). The
+** sqlite3FaultSim() function only returns non-zero during testing.
**
-** Return whatever integer value the test callback returns, or return
-** SQLITE_OK if no test callback is installed.
+** During testing, if the test harness has set a fault-sim callback using
+** a call to sqlite3_test_control(SQLITE_TESTCTRL_FAULT_INSTALL), then
+** each call to sqlite3FaultSim() is relayed to that application-supplied
+** callback and the integer return value form the application-supplied
+** callback is returned by sqlite3FaultSim().
+**
+** The integer argument to sqlite3FaultSim() is a code to identify which
+** sqlite3FaultSim() instance is being invoked. Each call to sqlite3FaultSim()
+** should have a unique code. To prevent legacy testing applications from
+** breaking, the codes should not be changed or reused.
*/
#ifndef SQLITE_UNTESTABLE
SQLITE_PRIVATE int sqlite3FaultSim(int iTest){
@@ -30328,6 +30429,19 @@ SQLITE_PRIVATE void sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){
}
/*
+** If database connection db is currently parsing SQL, then transfer
+** error code errCode to that parser if the parser has not already
+** encountered some other kind of error.
+*/
+SQLITE_PRIVATE int sqlite3ErrorToParser(sqlite3 *db, int errCode){
+ Parse *pParse;
+ if( db==0 || (pParse = db->pParse)==0 ) return errCode;
+ pParse->rc = errCode;
+ pParse->nErr++;
+ return errCode;
+}
+
+/*
** Convert an SQL-style quoted string into a normal string by removing
** the quote characters. The conversion is done in-place. If the
** input does not begin with a quote character, then this routine
@@ -31678,7 +31792,7 @@ SQLITE_PRIVATE VList *sqlite3VListAdd(
assert( pIn==0 || pIn[0]>=3 ); /* Verify ok to add new elements */
if( pIn==0 || pIn[1]+nInt > pIn[0] ){
/* Enlarge the allocation */
- int nAlloc = (pIn ? pIn[0]*2 : 10) + nInt;
+ sqlite3_int64 nAlloc = (pIn ? 2*(sqlite3_int64)pIn[0] : 10) + nInt;
VList *pOut = sqlite3DbRealloc(db, pIn, nAlloc*sizeof(int));
if( pOut==0 ) return pIn;
if( pIn==0 ) pOut[1] = 2;
@@ -31884,7 +31998,7 @@ static HashElem *findElementWithHash(
unsigned int *pHash /* Write the hash value here */
){
HashElem *elem; /* Used to loop thru the element list */
- int count; /* Number of elements left to test */
+ unsigned int count; /* Number of elements left to test */
unsigned int h; /* The computed hash */
static HashElem nullElement = { 0, 0, 0, 0 };
@@ -31932,8 +32046,8 @@ static void removeElementGivenHash(
if( pEntry->chain==elem ){
pEntry->chain = elem->next;
}
+ assert( pEntry->count>0 );
pEntry->count--;
- assert( pEntry->count>=0 );
}
sqlite3_free( elem );
pH->count--;
@@ -32108,25 +32222,25 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
/* 89 */ "Offset" OpHelp("r[P3] = sqlite_offset(P1)"),
/* 90 */ "Column" OpHelp("r[P3]=PX"),
/* 91 */ "Affinity" OpHelp("affinity(r[P1@P2])"),
- /* 92 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"),
- /* 93 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"),
- /* 94 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<<r[P1]"),
- /* 95 */ "ShiftRight" OpHelp("r[P3]=r[P2]>>r[P1]"),
- /* 96 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"),
- /* 97 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"),
- /* 98 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"),
- /* 99 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"),
- /* 100 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"),
- /* 101 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"),
- /* 102 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"),
- /* 103 */ "BitNot" OpHelp("r[P2]= ~r[P1]"),
- /* 104 */ "Count" OpHelp("r[P2]=count()"),
- /* 105 */ "ReadCookie" OpHelp(""),
- /* 106 */ "String8" OpHelp("r[P2]='P4'"),
- /* 107 */ "SetCookie" OpHelp(""),
- /* 108 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"),
- /* 109 */ "OpenRead" OpHelp("root=P2 iDb=P3"),
- /* 110 */ "OpenWrite" OpHelp("root=P2 iDb=P3"),
+ /* 92 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"),
+ /* 93 */ "Count" OpHelp("r[P2]=count()"),
+ /* 94 */ "ReadCookie" OpHelp(""),
+ /* 95 */ "SetCookie" OpHelp(""),
+ /* 96 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"),
+ /* 97 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"),
+ /* 98 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<<r[P1]"),
+ /* 99 */ "ShiftRight" OpHelp("r[P3]=r[P2]>>r[P1]"),
+ /* 100 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"),
+ /* 101 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"),
+ /* 102 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"),
+ /* 103 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"),
+ /* 104 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"),
+ /* 105 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"),
+ /* 106 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"),
+ /* 107 */ "BitNot" OpHelp("r[P2]= ~r[P1]"),
+ /* 108 */ "OpenRead" OpHelp("root=P2 iDb=P3"),
+ /* 109 */ "OpenWrite" OpHelp("root=P2 iDb=P3"),
+ /* 110 */ "String8" OpHelp("r[P2]='P4'"),
/* 111 */ "OpenDup" OpHelp(""),
/* 112 */ "OpenAutoindex" OpHelp("nColumn=P2"),
/* 113 */ "OpenEphemeral" OpHelp("nColumn=P2"),
@@ -32139,57 +32253,56 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
/* 120 */ "Sequence" OpHelp("r[P2]=cursor[P1].ctr++"),
/* 121 */ "NewRowid" OpHelp("r[P2]=rowid"),
/* 122 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"),
- /* 123 */ "InsertInt" OpHelp("intkey=P3 data=r[P2]"),
- /* 124 */ "Delete" OpHelp(""),
- /* 125 */ "ResetCount" OpHelp(""),
- /* 126 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"),
- /* 127 */ "SorterData" OpHelp("r[P2]=data"),
- /* 128 */ "RowData" OpHelp("r[P2]=data"),
- /* 129 */ "Rowid" OpHelp("r[P2]=rowid"),
- /* 130 */ "NullRow" OpHelp(""),
- /* 131 */ "SeekEnd" OpHelp(""),
- /* 132 */ "SorterInsert" OpHelp("key=r[P2]"),
- /* 133 */ "IdxInsert" OpHelp("key=r[P2]"),
- /* 134 */ "IdxDelete" OpHelp("key=r[P2@P3]"),
- /* 135 */ "DeferredSeek" OpHelp("Move P3 to P1.rowid if needed"),
- /* 136 */ "IdxRowid" OpHelp("r[P2]=rowid"),
- /* 137 */ "Destroy" OpHelp(""),
- /* 138 */ "Clear" OpHelp(""),
- /* 139 */ "ResetSorter" OpHelp(""),
- /* 140 */ "CreateBtree" OpHelp("r[P2]=root iDb=P1 flags=P3"),
- /* 141 */ "Real" OpHelp("r[P2]=P4"),
- /* 142 */ "SqlExec" OpHelp(""),
- /* 143 */ "ParseSchema" OpHelp(""),
- /* 144 */ "LoadAnalysis" OpHelp(""),
- /* 145 */ "DropTable" OpHelp(""),
- /* 146 */ "DropIndex" OpHelp(""),
- /* 147 */ "DropTrigger" OpHelp(""),
- /* 148 */ "IntegrityCk" OpHelp(""),
- /* 149 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"),
- /* 150 */ "Param" OpHelp(""),
- /* 151 */ "FkCounter" OpHelp("fkctr[P1]+=P2"),
- /* 152 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"),
- /* 153 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"),
- /* 154 */ "AggInverse" OpHelp("accum=r[P3] inverse(r[P2@P5])"),
- /* 155 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"),
- /* 156 */ "AggStep1" OpHelp("accum=r[P3] step(r[P2@P5])"),
- /* 157 */ "AggValue" OpHelp("r[P3]=value N=P2"),
- /* 158 */ "AggFinal" OpHelp("accum=r[P1] N=P2"),
- /* 159 */ "Expire" OpHelp(""),
- /* 160 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"),
- /* 161 */ "VBegin" OpHelp(""),
- /* 162 */ "VCreate" OpHelp(""),
- /* 163 */ "VDestroy" OpHelp(""),
- /* 164 */ "VOpen" OpHelp(""),
- /* 165 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"),
- /* 166 */ "VRename" OpHelp(""),
- /* 167 */ "Pagecount" OpHelp(""),
- /* 168 */ "MaxPgcnt" OpHelp(""),
- /* 169 */ "Trace" OpHelp(""),
- /* 170 */ "CursorHint" OpHelp(""),
- /* 171 */ "Noop" OpHelp(""),
- /* 172 */ "Explain" OpHelp(""),
- /* 173 */ "Abortable" OpHelp(""),
+ /* 123 */ "Delete" OpHelp(""),
+ /* 124 */ "ResetCount" OpHelp(""),
+ /* 125 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"),
+ /* 126 */ "SorterData" OpHelp("r[P2]=data"),
+ /* 127 */ "RowData" OpHelp("r[P2]=data"),
+ /* 128 */ "Rowid" OpHelp("r[P2]=rowid"),
+ /* 129 */ "NullRow" OpHelp(""),
+ /* 130 */ "SeekEnd" OpHelp(""),
+ /* 131 */ "SorterInsert" OpHelp("key=r[P2]"),
+ /* 132 */ "IdxInsert" OpHelp("key=r[P2]"),
+ /* 133 */ "IdxDelete" OpHelp("key=r[P2@P3]"),
+ /* 134 */ "DeferredSeek" OpHelp("Move P3 to P1.rowid if needed"),
+ /* 135 */ "IdxRowid" OpHelp("r[P2]=rowid"),
+ /* 136 */ "Destroy" OpHelp(""),
+ /* 137 */ "Clear" OpHelp(""),
+ /* 138 */ "ResetSorter" OpHelp(""),
+ /* 139 */ "CreateBtree" OpHelp("r[P2]=root iDb=P1 flags=P3"),
+ /* 140 */ "SqlExec" OpHelp(""),
+ /* 141 */ "ParseSchema" OpHelp(""),
+ /* 142 */ "LoadAnalysis" OpHelp(""),
+ /* 143 */ "DropTable" OpHelp(""),
+ /* 144 */ "DropIndex" OpHelp(""),
+ /* 145 */ "Real" OpHelp("r[P2]=P4"),
+ /* 146 */ "DropTrigger" OpHelp(""),
+ /* 147 */ "IntegrityCk" OpHelp(""),
+ /* 148 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"),
+ /* 149 */ "Param" OpHelp(""),
+ /* 150 */ "FkCounter" OpHelp("fkctr[P1]+=P2"),
+ /* 151 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"),
+ /* 152 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"),
+ /* 153 */ "AggInverse" OpHelp("accum=r[P3] inverse(r[P2@P5])"),
+ /* 154 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"),
+ /* 155 */ "AggStep1" OpHelp("accum=r[P3] step(r[P2@P5])"),
+ /* 156 */ "AggValue" OpHelp("r[P3]=value N=P2"),
+ /* 157 */ "AggFinal" OpHelp("accum=r[P1] N=P2"),
+ /* 158 */ "Expire" OpHelp(""),
+ /* 159 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"),
+ /* 160 */ "VBegin" OpHelp(""),
+ /* 161 */ "VCreate" OpHelp(""),
+ /* 162 */ "VDestroy" OpHelp(""),
+ /* 163 */ "VOpen" OpHelp(""),
+ /* 164 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"),
+ /* 165 */ "VRename" OpHelp(""),
+ /* 166 */ "Pagecount" OpHelp(""),
+ /* 167 */ "MaxPgcnt" OpHelp(""),
+ /* 168 */ "Trace" OpHelp(""),
+ /* 169 */ "CursorHint" OpHelp(""),
+ /* 170 */ "Noop" OpHelp(""),
+ /* 171 */ "Explain" OpHelp(""),
+ /* 172 */ "Abortable" OpHelp(""),
};
return azName[i];
}
@@ -49018,9 +49131,7 @@ static void pcache1FreePage(PgHdr1 *p){
** exists, this function falls back to sqlite3Malloc().
*/
SQLITE_PRIVATE void *sqlite3PageMalloc(int sz){
- /* During rebalance operations on a corrupt database file, it is sometimes
- ** (rarely) possible to overread the temporary page buffer by a few bytes.
- ** Enlarge the allocation slightly so that this does not cause problems. */
+ assert( sz<=65536+8 ); /* These allocations are never very large */
return pcache1Alloc(sz);
}
@@ -51304,6 +51415,9 @@ static const unsigned char aJournalMagic[] = {
SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){
if( pPager->fd->pMethods==0 ) return 0;
if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0;
+#ifdef SQLITE_HAS_CODEC
+ if( pPager->xCodec!=0 ) return 0;
+#endif
#ifndef SQLITE_OMIT_WAL
if( pPager->pWal ){
u32 iRead = 0;
@@ -54253,8 +54367,14 @@ SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nR
rc = sqlite3OsFileSize(pPager->fd, &nByte);
}
if( rc==SQLITE_OK ){
- pNew = (char *)sqlite3PageMalloc(pageSize);
- if( !pNew ) rc = SQLITE_NOMEM_BKPT;
+ /* 8 bytes of zeroed overrun space is sufficient so that the b-tree
+ * cell header parser will never run off the end of the allocation */
+ pNew = (char *)sqlite3PageMalloc(pageSize+8);
+ if( !pNew ){
+ rc = SQLITE_NOMEM_BKPT;
+ }else{
+ memset(pNew+pageSize, 0, 8);
+ }
}
if( rc==SQLITE_OK ){
@@ -57635,8 +57755,12 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i
*/
pPg->flags &= ~PGHDR_NEED_SYNC;
pPgOld = sqlite3PagerLookup(pPager, pgno);
- assert( !pPgOld || pPgOld->nRef==1 );
+ assert( !pPgOld || pPgOld->nRef==1 || CORRUPT_DB );
if( pPgOld ){
+ if( pPgOld->nRef>1 ){
+ sqlite3PagerUnrefNotNull(pPgOld);
+ return SQLITE_CORRUPT_BKPT;
+ }
pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC);
if( pPager->tempFile ){
/* Do not discard pages from an in-memory database since we might
@@ -58164,7 +58288,7 @@ SQLITE_PRIVATE int sqlite3PagerSnapshotCheck(Pager *pPager, sqlite3_snapshot *pS
*/
SQLITE_PRIVATE void sqlite3PagerSnapshotUnlock(Pager *pPager){
assert( pPager->pWal );
- return sqlite3WalSnapshotUnlock(pPager->pWal);
+ sqlite3WalSnapshotUnlock(pPager->pWal);
}
#endif /* SQLITE_ENABLE_SNAPSHOT */
@@ -58765,7 +58889,7 @@ static SQLITE_NOINLINE int walIndexPageRealloc(
/* Enlarge the pWal->apWiData[] array if required */
if( pWal->nWiData<=iPage ){
- int nByte = sizeof(u32*)*(iPage+1);
+ sqlite3_int64 nByte = sizeof(u32*)*(iPage+1);
volatile u32 **apNew;
apNew = (volatile u32 **)sqlite3_realloc64((void *)pWal->apWiData, nByte);
if( !apNew ){
@@ -58869,6 +58993,7 @@ static void walChecksumBytes(
assert( nByte>=8 );
assert( (nByte&0x00000007)==0 );
+ assert( nByte<=65536 );
if( nativeCksum ){
do {
@@ -59176,6 +59301,7 @@ static void walCleanupHash(Wal *pWal){
int iLimit = 0; /* Zero values greater than this */
int nByte; /* Number of bytes to zero in aPgno[] */
int i; /* Used to iterate through aHash[] */
+ int rc; /* Return code form walHashGet() */
assert( pWal->writeLock );
testcase( pWal->hdr.mxFrame==HASHTABLE_NPAGE_ONE-1 );
@@ -59186,11 +59312,12 @@ static void walCleanupHash(Wal *pWal){
/* Obtain pointers to the hash-table and page-number array containing
** the entry that corresponds to frame pWal->hdr.mxFrame. It is guaranteed
- ** that the page said hash-table and array reside on is already mapped.
+ ** that the page said hash-table and array reside on is already mapped.(1)
*/
assert( pWal->nWiData>walFramePage(pWal->hdr.mxFrame) );
assert( pWal->apWiData[walFramePage(pWal->hdr.mxFrame)] );
- walHashGet(pWal, walFramePage(pWal->hdr.mxFrame), &sLoc);
+ rc = walHashGet(pWal, walFramePage(pWal->hdr.mxFrame), &sLoc);
+ if( NEVER(rc) ) return; /* Defense-in-depth, in case (1) above is wrong */
/* Zero all hash-table entries that correspond to frame numbers greater
** than pWal->hdr.mxFrame.
@@ -59804,7 +59931,7 @@ static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){
WalIterator *p; /* Return value */
int nSegment; /* Number of segments to merge */
u32 iLast; /* Last frame in log */
- int nByte; /* Number of bytes to allocate */
+ sqlite3_int64 nByte; /* Number of bytes to allocate */
int i; /* Iterator variable */
ht_slot *aTmp; /* Temp space used by merge-sort */
int rc = SQLITE_OK; /* Return Code */
@@ -62340,7 +62467,7 @@ struct MemPage {
u16 maxLocal; /* Copy of BtShared.maxLocal or BtShared.maxLeaf */
u16 minLocal; /* Copy of BtShared.minLocal or BtShared.minLeaf */
u16 cellOffset; /* Index in aData of first cell pointer */
- u16 nFree; /* Number of free bytes on the page */
+ int nFree; /* Number of free bytes on the page. -1 for unknown */
u16 nCell; /* Number of cells on this page, local and ovfl */
u16 maskPage; /* Mask for page offset */
u16 aiOvfl[4]; /* Insert the i-th overflow cell before the aiOvfl-th
@@ -63894,14 +64021,18 @@ moveto_done:
*/
static int btreeRestoreCursorPosition(BtCursor *pCur){
int rc;
- int skipNext;
+ int skipNext = 0;
assert( cursorOwnsBtShared(pCur) );
assert( pCur->eState>=CURSOR_REQUIRESEEK );
if( pCur->eState==CURSOR_FAULT ){
return pCur->skipNext;
}
pCur->eState = CURSOR_INVALID;
- rc = btreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &skipNext);
+ if( sqlite3FaultSim(410) ){
+ rc = SQLITE_IOERR;
+ }else{
+ rc = btreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &skipNext);
+ }
if( rc==SQLITE_OK ){
sqlite3_free(pCur->pKey);
pCur->pKey = 0;
@@ -64482,7 +64613,7 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){
hdr = pPage->hdrOffset;
cellOffset = pPage->cellOffset;
nCell = pPage->nCell;
- assert( nCell==get2byte(&data[hdr+3]) );
+ assert( nCell==get2byte(&data[hdr+3]) || CORRUPT_DB );
iCellFirst = cellOffset + 2*nCell;
usableSize = pPage->pBt->usableSize;
@@ -64493,11 +64624,7 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){
** reconstruct the entire page. */
if( (int)data[hdr+7]<=nMaxFrag ){
int iFree = get2byte(&data[hdr+1]);
-
- /* If the initial freeblock offset were out of bounds, that would
- ** have been detected by btreeInitPage() when it was computing the
- ** number of free bytes on the page. */
- assert( iFree<=usableSize-4 );
+ if( iFree>usableSize-4 ) return SQLITE_CORRUPT_PAGE(pPage);
if( iFree ){
int iFree2 = get2byte(&data[iFree]);
if( iFree2>usableSize-4 ) return SQLITE_CORRUPT_PAGE(pPage);
@@ -64516,7 +64643,10 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){
if( iFree2+sz2 > usableSize ) return SQLITE_CORRUPT_PAGE(pPage);
memmove(&data[iFree+sz+sz2], &data[iFree+sz], iFree2-(iFree+sz));
sz += sz2;
+ }else if( iFree+sz>usableSize ){
+ return SQLITE_CORRUPT_PAGE(pPage);
}
+
cbrk = top+sz;
assert( cbrk+(iFree-top) <= usableSize );
memmove(&data[cbrk], &data[top], iFree-top);
@@ -64567,6 +64697,7 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){
data[hdr+7] = 0;
defragment_out:
+ assert( pPage->nFree>=0 );
if( data[hdr+7]+cbrk-iCellFirst!=pPage->nFree ){
return SQLITE_CORRUPT_PAGE(pPage);
}
@@ -64594,16 +64725,16 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){
** causes the fragmentation count to exceed 60.
*/
static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){
- const int hdr = pPg->hdrOffset;
- u8 * const aData = pPg->aData;
- int iAddr = hdr + 1;
- int pc = get2byte(&aData[iAddr]);
- int x;
- int usableSize = pPg->pBt->usableSize;
- int size; /* Size of the free slot */
+ const int hdr = pPg->hdrOffset; /* Offset to page header */
+ u8 * const aData = pPg->aData; /* Page data */
+ int iAddr = hdr + 1; /* Address of ptr to pc */
+ int pc = get2byte(&aData[iAddr]); /* Address of a free slot */
+ int x; /* Excess size of the slot */
+ int maxPC = pPg->pBt->usableSize - nByte; /* Max address for a usable slot */
+ int size; /* Size of the free slot */
assert( pc>0 );
- while( pc<=usableSize-4 ){
+ while( pc<=maxPC ){
/* EVIDENCE-OF: R-22710-53328 The third and fourth bytes of each
** freeblock form a big-endian integer which is the size of the freeblock
** in bytes, including the 4-byte header. */
@@ -64611,10 +64742,7 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){
if( (x = size - nByte)>=0 ){
testcase( x==4 );
testcase( x==3 );
- if( size+pc > usableSize ){
- *pRc = SQLITE_CORRUPT_PAGE(pPg);
- return 0;
- }else if( x<4 ){
+ if( x<4 ){
/* EVIDENCE-OF: R-11498-58022 In a well-formed b-tree page, the total
** number of bytes in fragments may not exceed 60. */
if( aData[hdr+7]>57 ) return 0;
@@ -64623,21 +64751,31 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){
** fragmented bytes within the page. */
memcpy(&aData[iAddr], &aData[pc], 2);
aData[hdr+7] += (u8)x;
+ }else if( x+pc > maxPC ){
+ /* This slot extends off the end of the usable part of the page */
+ *pRc = SQLITE_CORRUPT_PAGE(pPg);
+ return 0;
}else{
/* The slot remains on the free-list. Reduce its size to account
- ** for the portion used by the new allocation. */
+ ** for the portion used by the new allocation. */
put2byte(&aData[pc+2], x);
}
return &aData[pc + x];
}
iAddr = pc;
pc = get2byte(&aData[pc]);
- if( pc<iAddr+size ) break;
+ if( pc<=iAddr+size ){
+ if( pc ){
+ /* The next slot in the chain is not past the end of the current slot */
+ *pRc = SQLITE_CORRUPT_PAGE(pPg);
+ }
+ return 0;
+ }
}
- if( pc ){
+ if( pc>maxPC+nByte-4 ){
+ /* The free slot chain extends off the end of the page */
*pRc = SQLITE_CORRUPT_PAGE(pPg);
}
-
return 0;
}
@@ -64687,9 +64825,9 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
}
}
- /* If there is enough space between gap and top for one more cell pointer
- ** array entry offset, and if the freelist is not empty, then search the
- ** freelist looking for a free slot big enough to satisfy the request.
+ /* If there is enough space between gap and top for one more cell pointer,
+ ** and if the freelist is not empty, then search the
+ ** freelist looking for a slot big enough to satisfy the request.
*/
testcase( gap+2==top );
testcase( gap+1==top );
@@ -64711,6 +64849,7 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
testcase( gap+2+nByte==top );
if( gap+2+nByte>top ){
assert( pPage->nCell>0 || CORRUPT_DB );
+ assert( pPage->nFree>=0 );
rc = defragmentPage(pPage, MIN(4, pPage->nFree - (2+nByte)));
if( rc ) return rc;
top = get2byteNotZero(&data[hdr+5]);
@@ -64719,7 +64858,7 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
/* Allocate memory from the gap in between the cell pointer array
- ** and the cell content area. The btreeInitPage() call has already
+ ** and the cell content area. The btreeComputeFreeSpace() call has already
** validated the freelist. Given that the freelist is valid, there
** is no way that the allocation can extend off the end of the page.
** The assert() below verifies the previous sentence.
@@ -64738,7 +64877,7 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
**
** Adjacent freeblocks are coalesced.
**
-** Note that even though the freeblock list was checked by btreeInitPage(),
+** Even though the freeblock list was checked by btreeComputeFreeSpace(),
** that routine will not detect overlap between cells or freeblocks. Nor
** does it detect cells or freeblocks that encrouch into the reserved bytes
** at the end of the page. So do additional corruption checks inside this
@@ -64900,21 +65039,14 @@ static int decodeFlags(MemPage *pPage, int flagByte){
}
/*
-** Initialize the auxiliary information for a disk block.
-**
-** Return SQLITE_OK on success. If we see that the page does
-** not contain a well-formed database page, then return
-** SQLITE_CORRUPT. Note that a return of SQLITE_OK does not
-** guarantee that the page is well-formed. It only shows that
-** we failed to detect any corruption.
+** Compute the amount of freespace on the page. In other words, fill
+** in the pPage->nFree field.
*/
-static int btreeInitPage(MemPage *pPage){
+static int btreeComputeFreeSpace(MemPage *pPage){
int pc; /* Address of a freeblock within pPage->aData[] */
u8 hdr; /* Offset to beginning of page header */
u8 *data; /* Equal to pPage->aData */
- BtShared *pBt; /* The main btree structure */
int usableSize; /* Amount of usable space on each page */
- u16 cellOffset; /* Offset from start of page to first cell pointer */
int nFree; /* Number of unused bytes on the page */
int top; /* First byte of the cell content area */
int iCellFirst; /* First allowable cell or freeblock offset */
@@ -64926,71 +65058,18 @@ static int btreeInitPage(MemPage *pPage){
assert( pPage->pgno==sqlite3PagerPagenumber(pPage->pDbPage) );
assert( pPage == sqlite3PagerGetExtra(pPage->pDbPage) );
assert( pPage->aData == sqlite3PagerGetData(pPage->pDbPage) );
- assert( pPage->isInit==0 );
+ assert( pPage->isInit==1 );
+ assert( pPage->nFree<0 );
- pBt = pPage->pBt;
+ usableSize = pPage->pBt->usableSize;
hdr = pPage->hdrOffset;
data = pPage->aData;
- /* EVIDENCE-OF: R-28594-02890 The one-byte flag at offset 0 indicating
- ** the b-tree page type. */
- if( decodeFlags(pPage, data[hdr]) ){
- return SQLITE_CORRUPT_PAGE(pPage);
- }
- assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );
- pPage->maskPage = (u16)(pBt->pageSize - 1);
- pPage->nOverflow = 0;
- usableSize = pBt->usableSize;
- pPage->cellOffset = cellOffset = hdr + 8 + pPage->childPtrSize;
- pPage->aDataEnd = &data[usableSize];
- pPage->aCellIdx = &data[cellOffset];
- pPage->aDataOfst = &data[pPage->childPtrSize];
/* EVIDENCE-OF: R-58015-48175 The two-byte integer at offset 5 designates
** the start of the cell content area. A zero value for this integer is
** interpreted as 65536. */
top = get2byteNotZero(&data[hdr+5]);
- /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the
- ** number of cells on the page. */
- pPage->nCell = get2byte(&data[hdr+3]);
- if( pPage->nCell>MX_CELL(pBt) ){
- /* To many cells for a single page. The page must be corrupt */
- return SQLITE_CORRUPT_PAGE(pPage);
- }
- testcase( pPage->nCell==MX_CELL(pBt) );
- /* EVIDENCE-OF: R-24089-57979 If a page contains no cells (which is only
- ** possible for a root page of a table that contains no rows) then the
- ** offset to the cell content area will equal the page size minus the
- ** bytes of reserved space. */
- assert( pPage->nCell>0 || top==usableSize || CORRUPT_DB );
-
- /* A malformed database page might cause us to read past the end
- ** of page when parsing a cell.
- **
- ** The following block of code checks early to see if a cell extends
- ** past the end of a page boundary and causes SQLITE_CORRUPT to be
- ** returned if it does.
- */
- iCellFirst = cellOffset + 2*pPage->nCell;
+ iCellFirst = hdr + 8 + pPage->childPtrSize + 2*pPage->nCell;
iCellLast = usableSize - 4;
- if( pBt->db->flags & SQLITE_CellSizeCk ){
- int i; /* Index into the cell pointer array */
- int sz; /* Size of a cell */
-
- if( !pPage->leaf ) iCellLast--;
- for(i=0; i<pPage->nCell; i++){
- pc = get2byteAligned(&data[cellOffset+i*2]);
- testcase( pc==iCellFirst );
- testcase( pc==iCellLast );
- if( pc<iCellFirst || pc>iCellLast ){
- return SQLITE_CORRUPT_PAGE(pPage);
- }
- sz = pPage->xCellSize(pPage, &data[pc]);
- testcase( pc+sz==usableSize );
- if( pc+sz>usableSize ){
- return SQLITE_CORRUPT_PAGE(pPage);
- }
- }
- if( !pPage->leaf ) iCellLast++;
- }
/* Compute the total free space on the page
** EVIDENCE-OF: R-23588-34450 The two-byte integer at offset 1 gives the
@@ -65038,7 +65117,100 @@ static int btreeInitPage(MemPage *pPage){
return SQLITE_CORRUPT_PAGE(pPage);
}
pPage->nFree = (u16)(nFree - iCellFirst);
+ return SQLITE_OK;
+}
+
+/*
+** Do additional sanity check after btreeInitPage() if
+** PRAGMA cell_size_check=ON
+*/
+static SQLITE_NOINLINE int btreeCellSizeCheck(MemPage *pPage){
+ int iCellFirst; /* First allowable cell or freeblock offset */
+ int iCellLast; /* Last possible cell or freeblock offset */
+ int i; /* Index into the cell pointer array */
+ int sz; /* Size of a cell */
+ int pc; /* Address of a freeblock within pPage->aData[] */
+ u8 *data; /* Equal to pPage->aData */
+ int usableSize; /* Maximum usable space on the page */
+ int cellOffset; /* Start of cell content area */
+
+ iCellFirst = pPage->cellOffset + 2*pPage->nCell;
+ usableSize = pPage->pBt->usableSize;
+ iCellLast = usableSize - 4;
+ data = pPage->aData;
+ cellOffset = pPage->cellOffset;
+ if( !pPage->leaf ) iCellLast--;
+ for(i=0; i<pPage->nCell; i++){
+ pc = get2byteAligned(&data[cellOffset+i*2]);
+ testcase( pc==iCellFirst );
+ testcase( pc==iCellLast );
+ if( pc<iCellFirst || pc>iCellLast ){
+ return SQLITE_CORRUPT_PAGE(pPage);
+ }
+ sz = pPage->xCellSize(pPage, &data[pc]);
+ testcase( pc+sz==usableSize );
+ if( pc+sz>usableSize ){
+ return SQLITE_CORRUPT_PAGE(pPage);
+ }
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Initialize the auxiliary information for a disk block.
+**
+** Return SQLITE_OK on success. If we see that the page does
+** not contain a well-formed database page, then return
+** SQLITE_CORRUPT. Note that a return of SQLITE_OK does not
+** guarantee that the page is well-formed. It only shows that
+** we failed to detect any corruption.
+*/
+static int btreeInitPage(MemPage *pPage){
+ u8 *data; /* Equal to pPage->aData */
+ BtShared *pBt; /* The main btree structure */
+
+ assert( pPage->pBt!=0 );
+ assert( pPage->pBt->db!=0 );
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+ assert( pPage->pgno==sqlite3PagerPagenumber(pPage->pDbPage) );
+ assert( pPage == sqlite3PagerGetExtra(pPage->pDbPage) );
+ assert( pPage->aData == sqlite3PagerGetData(pPage->pDbPage) );
+ assert( pPage->isInit==0 );
+
+ pBt = pPage->pBt;
+ data = pPage->aData + pPage->hdrOffset;
+ /* EVIDENCE-OF: R-28594-02890 The one-byte flag at offset 0 indicating
+ ** the b-tree page type. */
+ if( decodeFlags(pPage, data[0]) ){
+ return SQLITE_CORRUPT_PAGE(pPage);
+ }
+ assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );
+ pPage->maskPage = (u16)(pBt->pageSize - 1);
+ pPage->nOverflow = 0;
+ pPage->cellOffset = pPage->hdrOffset + 8 + pPage->childPtrSize;
+ pPage->aCellIdx = data + pPage->childPtrSize + 8;
+ pPage->aDataEnd = pPage->aData + pBt->usableSize;
+ pPage->aDataOfst = pPage->aData + pPage->childPtrSize;
+ /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the
+ ** number of cells on the page. */
+ pPage->nCell = get2byte(&data[3]);
+ if( pPage->nCell>MX_CELL(pBt) ){
+ /* To many cells for a single page. The page must be corrupt */
+ return SQLITE_CORRUPT_PAGE(pPage);
+ }
+ testcase( pPage->nCell==MX_CELL(pBt) );
+ /* EVIDENCE-OF: R-24089-57979 If a page contains no cells (which is only
+ ** possible for a root page of a table that contains no rows) then the
+ ** offset to the cell content area will equal the page size minus the
+ ** bytes of reserved space. */
+ assert( pPage->nCell>0
+ || get2byteNotZero(&data[5])==(int)pBt->usableSize
+ || CORRUPT_DB );
+ pPage->nFree = -1; /* Indicate that this value is yet uncomputed */
pPage->isInit = 1;
+ if( pBt->db->flags & SQLITE_CellSizeCk ){
+ return btreeCellSizeCheck(pPage);
+ }
return SQLITE_OK;
}
@@ -65181,19 +65353,18 @@ static int getAndInitPage(
if( pgno>btreePagecount(pBt) ){
rc = SQLITE_CORRUPT_BKPT;
- goto getAndInitPage_error;
+ goto getAndInitPage_error1;
}
rc = sqlite3PagerGet(pBt->pPager, pgno, (DbPage**)&pDbPage, bReadOnly);
if( rc ){
- goto getAndInitPage_error;
+ goto getAndInitPage_error1;
}
*ppPage = (MemPage*)sqlite3PagerGetExtra(pDbPage);
if( (*ppPage)->isInit==0 ){
btreePageFromDbPage(pDbPage, pgno, pBt);
rc = btreeInitPage(*ppPage);
if( rc!=SQLITE_OK ){
- releasePage(*ppPage);
- goto getAndInitPage_error;
+ goto getAndInitPage_error2;
}
}
assert( (*ppPage)->pgno==pgno );
@@ -65203,12 +65374,13 @@ static int getAndInitPage(
** compatible with the root page. */
if( pCur && ((*ppPage)->nCell<1 || (*ppPage)->intKey!=pCur->curIntKey) ){
rc = SQLITE_CORRUPT_PGNO(pgno);
- releasePage(*ppPage);
- goto getAndInitPage_error;
+ goto getAndInitPage_error2;
}
return SQLITE_OK;
-getAndInitPage_error:
+getAndInitPage_error2:
+ releasePage(*ppPage);
+getAndInitPage_error1:
if( pCur ){
pCur->iPage--;
pCur->pPage = pCur->apPage[pCur->iPage];
@@ -68289,23 +68461,6 @@ SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
return rc;
}
-/*
-** This function is a no-op if cursor pCur does not point to a valid row.
-** Otherwise, if pCur is valid, configure it so that the next call to
-** sqlite3BtreeNext() is a no-op.
-*/
-#ifndef SQLITE_OMIT_WINDOWFUNC
-SQLITE_PRIVATE void sqlite3BtreeSkipNext(BtCursor *pCur){
- /* We believe that the cursor must always be in the valid state when
- ** this routine is called, but the proof is difficult, so we add an
- ** ALWaYS() test just in case we are wrong. */
- if( ALWAYS(pCur->eState==CURSOR_VALID) ){
- pCur->eState = CURSOR_SKIPNEXT;
- pCur->skipNext = 1;
- }
-}
-#endif /* SQLITE_OMIT_WINDOWFUNC */
-
/* Move the cursor to the last entry in the table. Return SQLITE_OK
** on success. Set *pRes to 0 if the cursor actually points to something
** or set *pRes to 1 if the table is empty.
@@ -68571,7 +68726,7 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
sqlite3_free(pCellKey);
goto moveto_finish;
}
- c = xRecordCompare(nCell, pCellKey, pIdxKey);
+ c = sqlite3VdbeRecordCompare(nCell, pCellKey, pIdxKey);
sqlite3_free(pCellKey);
}
assert(
@@ -69203,13 +69358,15 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
MemPage *pPage1 = pBt->pPage1; /* Local reference to page 1 */
MemPage *pPage; /* Page being freed. May be NULL. */
int rc; /* Return Code */
- int nFree; /* Initial number of pages on free-list */
+ u32 nFree; /* Initial number of pages on free-list */
assert( sqlite3_mutex_held(pBt->mutex) );
assert( CORRUPT_DB || iPage>1 );
assert( !pMemPage || pMemPage->pgno==iPage );
- if( iPage<2 ) return SQLITE_CORRUPT_BKPT;
+ if( iPage<2 || iPage>pBt->nPage ){
+ return SQLITE_CORRUPT_BKPT;
+ }
if( pMemPage ){
pPage = pMemPage;
sqlite3PagerRef(pPage->pDbPage);
@@ -69620,6 +69777,7 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
assert( CORRUPT_DB || sz==cellSize(pPage, idx) );
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+ assert( pPage->nFree>=0 );
data = pPage->aData;
ptr = &pPage->aCellIdx[2*idx];
pc = get2byte(ptr);
@@ -69690,6 +69848,7 @@ static void insertCell(
** might be less than 8 (leaf-size + pointer) on the interior node. Hence
** the term after the || in the following assert(). */
assert( sz==pPage->xCellSize(pPage, pCell) || (sz==8 && iChild>0) );
+ assert( pPage->nFree>=0 );
if( pPage->nOverflow || sz+2>pPage->nFree ){
if( pTemp ){
memcpy(pTemp, pCell, sz);
@@ -69747,7 +69906,7 @@ static void insertCell(
pPage->nCell++;
/* increment the cell count */
if( (++data[pPage->hdrOffset+4])==0 ) data[pPage->hdrOffset+3]++;
- assert( get2byte(&data[pPage->hdrOffset+3])==pPage->nCell );
+ assert( get2byte(&data[pPage->hdrOffset+3])==pPage->nCell || CORRUPT_DB );
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pPage->pBt->autoVacuum ){
/* The cell may contain a pointer to an overflow page. If so, write
@@ -69834,8 +69993,13 @@ static void insertCell(
** are used and they point to the leaf pages only, and the ixNx value are:
**
** ixNx[0] = Number of cells in Child-1.
-** ixNx[1] = Number of cells in Child-1 and Child-2 + 1 for 1st divider.
-** ixNx[2] = Number of cells in Child-1 and Child-2 + both divider cells
+** ixNx[1] = Number of cells in Child-1 and Child-2.
+** ixNx[2] = Total number of cells.
+**
+** Sometimes when deleting, a child page can have zero cells. In those
+** cases, ixNx[] entries with higher indexes, and the corresponding apEnd[]
+** entries, shift down. The end result is that each ixNx[] entry should
+** be larger than the previous
*/
typedef struct CellArray CellArray;
struct CellArray {
@@ -70164,8 +70328,9 @@ static int editPage(
int iCell = (iOld + pPg->aiOvfl[i]) - iNew;
if( iCell>=0 && iCell<nNew ){
pCellptr = &pPg->aCellIdx[iCell * 2];
- assert( nCell>=iCell );
- memmove(&pCellptr[2], pCellptr, (nCell - iCell) * 2);
+ if( nCell>iCell ){
+ memmove(&pCellptr[2], pCellptr, (nCell - iCell) * 2);
+ }
nCell++;
if( pageInsertArray(
pPg, pBegin, &pData, pCellptr,
@@ -70241,8 +70406,10 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
assert( sqlite3PagerIswriteable(pParent->pDbPage) );
assert( pPage->nOverflow==1 );
-
+
if( pPage->nCell==0 ) return SQLITE_CORRUPT_BKPT; /* dbfuzz001.test */
+ assert( pPage->nFree>=0 );
+ assert( pParent->nFree>=0 );
/* Allocate a new page. This page will become the right-sibling of
** pPage. Make the parent page writable, so that the new divider cell
@@ -70412,6 +70579,7 @@ static void copyNodeContent(MemPage *pFrom, MemPage *pTo, int *pRC){
*/
pTo->isInit = 0;
rc = btreeInitPage(pTo);
+ if( rc==SQLITE_OK ) rc = btreeComputeFreeSpace(pTo);
if( rc!=SQLITE_OK ){
*pRC = rc;
return;
@@ -70520,6 +70688,7 @@ static int balance_nonroot(
if( !aOvflSpace ){
return SQLITE_NOMEM_BKPT;
}
+ assert( pParent->nFree>=0 );
/* Find the sibling pages to balance. Also locate the cells in pParent
** that divide the siblings. An attempt is made to find NN siblings on
@@ -70559,7 +70728,13 @@ static int balance_nonroot(
memset(apOld, 0, (i+1)*sizeof(MemPage*));
goto balance_cleanup;
}
- nMaxCells += 1+apOld[i]->nCell+apOld[i]->nOverflow;
+ if( apOld[i]->nFree<0 ){
+ rc = btreeComputeFreeSpace(apOld[i]);
+ if( rc ){
+ memset(apOld, 0, (i)*sizeof(MemPage*));
+ goto balance_cleanup;
+ }
+ }
if( (i--)==0 ) break;
if( pParent->nOverflow && i+nxDiv==pParent->aiOvfl[0] ){
@@ -70603,6 +70778,7 @@ static int balance_nonroot(
/* Make nMaxCells a multiple of 4 in order to preserve 8-byte
** alignment */
+ nMaxCells = nOld*(MX_CELL(pBt) + ArraySize(pParent->apOvfl));
nMaxCells = (nMaxCells + 3)&~3;
/*
@@ -70613,7 +70789,7 @@ static int balance_nonroot(
+ nMaxCells*sizeof(u16) /* b.szCell */
+ pBt->pageSize; /* aSpace1 */
- assert( szScratch<=6*(int)pBt->pageSize );
+ assert( szScratch<=7*(int)pBt->pageSize );
b.apCell = sqlite3StackAllocRaw(0, szScratch );
if( b.apCell==0 ){
rc = SQLITE_NOMEM_BKPT;
@@ -70753,11 +70929,15 @@ static int balance_nonroot(
MemPage *p = apOld[i];
b.apEnd[k] = p->aDataEnd;
b.ixNx[k] = cntOld[i];
+ if( k && b.ixNx[k]==b.ixNx[k-1] ){
+ k--; /* Omit b.ixNx[] entry for child pages with no cells */
+ }
if( !leafData ){
k++;
b.apEnd[k] = pParent->aDataEnd;
b.ixNx[k] = cntOld[i]+1;
}
+ assert( p->nFree>=0 );
szNew[i] = usableSpace - p->nFree;
for(j=0; j<p->nOverflow; j++){
szNew[i] += 2 + p->xCellSize(p, p->apOvfl[j]);
@@ -70983,18 +71163,17 @@ static int balance_nonroot(
if( ISAUTOVACUUM ){
MemPage *pOld;
MemPage *pNew = pOld = apNew[0];
- u8 *aOld = pNew->aData;
int cntOldNext = pNew->nCell + pNew->nOverflow;
- int usableSize = pBt->usableSize;
int iNew = 0;
int iOld = 0;
for(i=0; i<b.nCell; i++){
u8 *pCell = b.apCell[i];
- if( i==cntOldNext ){
- pOld = (++iOld)<nNew ? apNew[iOld] : apOld[iOld];
+ while( i==cntOldNext ){
+ iOld++;
+ assert( iOld<nNew || iOld<nOld );
+ pOld = iOld<nNew ? apNew[iOld] : apOld[iOld];
cntOldNext += pOld->nCell + pOld->nOverflow + !leafData;
- aOld = pOld->aData;
}
if( i==cntNew[iNew] ){
pNew = apNew[++iNew];
@@ -71009,7 +71188,7 @@ static int balance_nonroot(
** overflow cell), we can skip updating the pointer map entries. */
if( iOld>=nNew
|| pNew->pgno!=aPgno[iOld]
- || !SQLITE_WITHIN(pCell,aOld,&aOld[usableSize])
+ || !SQLITE_WITHIN(pCell,pOld->aData,pOld->aDataEnd)
){
if( !leafCorrection ){
ptrmapPut(pBt, get4byte(pCell), PTRMAP_BTREE, pNew->pgno, &rc);
@@ -71160,7 +71339,8 @@ static int balance_nonroot(
rc = defragmentPage(apNew[0], -1);
testcase( rc!=SQLITE_OK );
assert( apNew[0]->nFree ==
- (get2byte(&apNew[0]->aData[5])-apNew[0]->cellOffset-apNew[0]->nCell*2)
+ (get2byteNotZero(&apNew[0]->aData[5]) - apNew[0]->cellOffset
+ - apNew[0]->nCell*2)
|| rc!=SQLITE_OK
);
copyNodeContent(apNew[0], pParent, &rc);
@@ -71259,7 +71439,7 @@ static int balance_deeper(MemPage *pRoot, MemPage **ppChild){
}
assert( sqlite3PagerIswriteable(pChild->pDbPage) );
assert( sqlite3PagerIswriteable(pRoot->pDbPage) );
- assert( pChild->nCell==pRoot->nCell );
+ assert( pChild->nCell==pRoot->nCell || CORRUPT_DB );
TRACE(("BALANCE: copy root %d into %d\n", pRoot->pgno, pChild->pgno));
@@ -71301,6 +71481,7 @@ static int balance(BtCursor *pCur){
int iPage = pCur->iPage;
MemPage *pPage = pCur->pPage;
+ if( NEVER(pPage->nFree<0) && btreeComputeFreeSpace(pPage) ) break;
if( iPage==0 ){
if( pPage->nOverflow ){
/* The root page of the b-tree is overfull. In this case call the
@@ -71329,6 +71510,9 @@ static int balance(BtCursor *pCur){
int const iIdx = pCur->aiIdx[iPage-1];
rc = sqlite3PagerWrite(pParent->pDbPage);
+ if( rc==SQLITE_OK && pParent->nFree<0 ){
+ rc = btreeComputeFreeSpace(pParent);
+ }
if( rc==SQLITE_OK ){
#ifndef SQLITE_OMIT_QUICKBALANCE
if( pPage->intKeyLeaf
@@ -71675,6 +71859,10 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
pPage = pCur->pPage;
assert( pPage->intKey || pX->nKey>=0 );
assert( pPage->leaf || !pPage->intKey );
+ if( pPage->nFree<0 ){
+ rc = btreeComputeFreeSpace(pPage);
+ if( rc ) return rc;
+ }
TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n",
pCur->pgnoRoot, pX->nKey, pX->nData, pPage->pgno,
@@ -71817,14 +72005,18 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
assert( pCur->curFlags & BTCF_WriteFlag );
assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) );
assert( !hasReadConflicts(p, pCur->pgnoRoot) );
- assert( pCur->ix<pCur->pPage->nCell );
- assert( pCur->eState==CURSOR_VALID );
assert( (flags & ~(BTREE_SAVEPOSITION | BTREE_AUXDELETE))==0 );
+ if( pCur->eState==CURSOR_REQUIRESEEK ){
+ rc = btreeRestoreCursorPosition(pCur);
+ if( rc ) return rc;
+ }
+ assert( pCur->eState==CURSOR_VALID );
iCellDepth = pCur->iPage;
iCellIdx = pCur->ix;
pPage = pCur->pPage;
pCell = findCell(pPage, iCellIdx);
+ if( pPage->nFree<0 && btreeComputeFreeSpace(pPage) ) return SQLITE_CORRUPT;
/* If the bPreserve flag is set to true, then the cursor position must
** be preserved following this delete operation. If the current delete
@@ -71895,6 +72087,10 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
Pgno n;
unsigned char *pTmp;
+ if( pLeaf->nFree<0 ){
+ rc = btreeComputeFreeSpace(pLeaf);
+ if( rc ) return rc;
+ }
if( iCellDepth<pCur->iPage-1 ){
n = pCur->apPage[iCellDepth+1]->pgno;
}else{
@@ -72253,6 +72449,9 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){
assert( sqlite3BtreeHoldsMutex(p) );
assert( p->inTrans==TRANS_WRITE );
assert( iTable>=2 );
+ if( iTable>btreePagecount(pBt) ){
+ return SQLITE_CORRUPT_BKPT;
+ }
rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0);
if( rc ) return rc;
@@ -72601,10 +72800,10 @@ static void checkList(
IntegrityCk *pCheck, /* Integrity checking context */
int isFreeList, /* True for a freelist. False for overflow page list */
int iPage, /* Page number for first page in the list */
- int N /* Expected number of pages in the list */
+ u32 N /* Expected number of pages in the list */
){
int i;
- int expected = N;
+ u32 expected = N;
int nErrAtStart = pCheck->nErr;
while( iPage!=0 && pCheck->mxErr ){
DbPage *pOvflPage;
@@ -72786,6 +72985,11 @@ static int checkTreePage(
"btreeInitPage() returns error code %d", rc);
goto end_of_check;
}
+ if( (rc = btreeComputeFreeSpace(pPage))!=0 ){
+ assert( rc==SQLITE_CORRUPT );
+ checkAppendMsg(pCheck, "free space corruption", rc);
+ goto end_of_check;
+ }
data = pPage->aData;
hdr = pPage->hdrOffset;
@@ -72858,7 +73062,7 @@ static int checkTreePage(
/* Check the content overflow list */
if( info.nPayload>info.nLocal ){
- int nPage; /* Number of pages on the overflow chain */
+ u32 nPage; /* Number of pages on the overflow chain */
Pgno pgnoOvfl; /* First page of the overflow chain */
assert( pc + info.nSize - 4 <= usableSize );
nPage = (info.nPayload - info.nLocal + usableSize - 5)/(usableSize - 4);
@@ -72918,9 +73122,9 @@ static int checkTreePage(
i = get2byte(&data[hdr+1]);
while( i>0 ){
int size, j;
- assert( (u32)i<=usableSize-4 ); /* Enforced by btreeInitPage() */
+ assert( (u32)i<=usableSize-4 ); /* Enforced by btreeComputeFreeSpace() */
size = get2byte(&data[i+2]);
- assert( (u32)(i+size)<=usableSize ); /* Enforced by btreeInitPage() */
+ assert( (u32)(i+size)<=usableSize ); /* due to btreeComputeFreeSpace() */
btreeHeapInsert(heap, (((u32)i)<<16)|(i+size-1));
/* EVIDENCE-OF: R-58208-19414 The first 2 bytes of a freeblock are a
** big-endian integer which is the offset in the b-tree page of the next
@@ -72929,8 +73133,8 @@ static int checkTreePage(
j = get2byte(&data[i]);
/* EVIDENCE-OF: R-06866-39125 Freeblocks are always connected in order of
** increasing offset. */
- assert( j==0 || j>i+size ); /* Enforced by btreeInitPage() */
- assert( (u32)j<=usableSize-4 ); /* Enforced by btreeInitPage() */
+ assert( j==0 || j>i+size ); /* Enforced by btreeComputeFreeSpace() */
+ assert( (u32)j<=usableSize-4 ); /* Enforced by btreeComputeFreeSpace() */
i = j;
}
/* Analyze the min-heap looking for overlap between cells and/or
@@ -74274,7 +74478,7 @@ SQLITE_PRIVATE int sqlite3VdbeCheckMemInvariants(Mem *p){
((p->flags&MEM_Static)!=0 ? 1 : 0) <= 1 );
/* No other bits set */
- assert( (p->flags & ~(MEM_Null|MEM_Term|MEM_Subtype
+ assert( (p->flags & ~(MEM_Null|MEM_Term|MEM_Subtype|MEM_FromBind
|MEM_Dyn|MEM_Ephem|MEM_Static))==0 );
}else{
/* A pure NULL might have other flags, such as MEM_Static, MEM_Dyn,
@@ -74395,8 +74599,7 @@ SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){
}
/*
-** Make sure pMem->z points to a writable allocation of at least
-** min(n,32) bytes.
+** Make sure pMem->z points to a writable allocation of at least n bytes.
**
** If the bPreserve argument is true, then copy of the content of
** pMem->z into the new allocation. pMem must be either a string or
@@ -74415,7 +74618,6 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPre
assert( pMem->szMalloc==0
|| pMem->szMalloc==sqlite3DbMallocSize(pMem->db, pMem->zMalloc) );
- if( n<32 ) n = 32;
if( pMem->szMalloc>0 && bPreserve && pMem->z==pMem->zMalloc ){
pMem->z = pMem->zMalloc = sqlite3DbReallocOrFree(pMem->db, pMem->z, n);
bPreserve = 0;
@@ -74517,13 +74719,15 @@ SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem *pMem){
SQLITE_PRIVATE int sqlite3VdbeMemExpandBlob(Mem *pMem){
int nByte;
assert( pMem->flags & MEM_Zero );
- assert( pMem->flags&MEM_Blob );
+ assert( (pMem->flags&MEM_Blob)!=0 || MemNullNochng(pMem) );
+ testcase( sqlite3_value_nochange(pMem) );
assert( !sqlite3VdbeMemIsRowSet(pMem) );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
/* Set nByte to the number of bytes required to store the expanded blob. */
nByte = pMem->n + pMem->u.nZero;
if( nByte<=0 ){
+ if( (pMem->flags & MEM_Blob)==0 ) return SQLITE_OK;
nByte = 1;
}
if( sqlite3VdbeMemGrow(pMem, nByte, 1) ){
@@ -75264,7 +75468,6 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr(
assert( enc!=0 );
if( enc==SQLITE_UTF8 ){
nByte = 0x7fffffff & (int)strlen(z);
- if( nByte>iLimit ) nByte = iLimit+1;
}else{
for(nByte=0; nByte<=iLimit && (z[nByte] | z[nByte+1]); nByte+=2){}
}
@@ -75276,29 +75479,30 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr(
** management (one of MEM_Dyn or MEM_Static).
*/
if( xDel==SQLITE_TRANSIENT ){
- int nAlloc = nByte;
+ u32 nAlloc = nByte;
if( flags&MEM_Term ){
nAlloc += (enc==SQLITE_UTF8?1:2);
}
if( nByte>iLimit ){
- return SQLITE_TOOBIG;
+ return sqlite3ErrorToParser(pMem->db, SQLITE_TOOBIG);
}
testcase( nAlloc==0 );
testcase( nAlloc==31 );
testcase( nAlloc==32 );
- if( sqlite3VdbeMemClearAndResize(pMem, MAX(nAlloc,32)) ){
+ if( sqlite3VdbeMemClearAndResize(pMem, (int)MAX(nAlloc,32)) ){
return SQLITE_NOMEM_BKPT;
}
memcpy(pMem->z, z, nAlloc);
- }else if( xDel==SQLITE_DYNAMIC ){
- sqlite3VdbeMemRelease(pMem);
- pMem->zMalloc = pMem->z = (char *)z;
- pMem->szMalloc = sqlite3DbMallocSize(pMem->db, pMem->zMalloc);
}else{
sqlite3VdbeMemRelease(pMem);
pMem->z = (char *)z;
- pMem->xDel = xDel;
- flags |= ((xDel==SQLITE_STATIC)?MEM_Static:MEM_Dyn);
+ if( xDel==SQLITE_DYNAMIC ){
+ pMem->zMalloc = pMem->z;
+ pMem->szMalloc = sqlite3DbMallocSize(pMem->db, pMem->zMalloc);
+ }else{
+ pMem->xDel = xDel;
+ flags |= ((xDel==SQLITE_STATIC)?MEM_Static:MEM_Dyn);
+ }
}
pMem->n = nByte;
@@ -76266,9 +76470,11 @@ static int growOpArray(Vdbe *v, int nOp){
** operation (without SQLITE_TEST_REALLOC_STRESS) is to double the current
** size of the op array or add 1KB of space, whichever is smaller. */
#ifdef SQLITE_TEST_REALLOC_STRESS
- int nNew = (v->nOpAlloc>=512 ? v->nOpAlloc*2 : v->nOpAlloc+nOp);
+ sqlite3_int64 nNew = (v->nOpAlloc>=512 ? 2*(sqlite3_int64)v->nOpAlloc
+ : (sqlite3_int64)v->nOpAlloc+nOp);
#else
- int nNew = (v->nOpAlloc ? v->nOpAlloc*2 : (int)(1024/sizeof(Op)));
+ sqlite3_int64 nNew = (v->nOpAlloc ? 2*(sqlite3_int64)v->nOpAlloc
+ : (sqlite3_int64)(1024/sizeof(Op)));
UNUSED_PARAMETER(nOp);
#endif
@@ -76748,6 +76954,7 @@ SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){
int opcode = pOp->opcode;
if( opcode==OP_Destroy || opcode==OP_VUpdate || opcode==OP_VRename
|| opcode==OP_VDestroy
+ || (opcode==OP_Function0 && pOp->p4.pFunc->funcFlags&SQLITE_FUNC_INTERNAL)
|| ((opcode==OP_Halt || opcode==OP_HaltIfNull)
&& ((pOp->p1)!=SQLITE_OK && pOp->p2==OE_Abort))
){
@@ -77055,7 +77262,7 @@ SQLITE_PRIVATE void sqlite3VdbeScanStatus(
LogEst nEst, /* Estimated number of output rows */
const char *zName /* Name of table or index being scanned */
){
- int nByte = (p->nScan+1) * sizeof(ScanStatus);
+ sqlite3_int64 nByte = (p->nScan+1) * sizeof(ScanStatus);
ScanStatus *aNew;
aNew = (ScanStatus*)sqlite3DbRealloc(p->db, p->aScan, nByte);
if( aNew ){
@@ -78176,9 +78383,9 @@ SQLITE_PRIVATE void sqlite3VdbeIOTraceSql(Vdbe *p){
** of a ReusableSpace object by the allocSpace() routine below.
*/
struct ReusableSpace {
- u8 *pSpace; /* Available memory */
- int nFree; /* Bytes of available memory */
- int nNeeded; /* Total bytes that could not be allocated */
+ u8 *pSpace; /* Available memory */
+ sqlite3_int64 nFree; /* Bytes of available memory */
+ sqlite3_int64 nNeeded; /* Total bytes that could not be allocated */
};
/* Try to allocate nByte bytes of 8-byte aligned bulk memory for pBuf
@@ -78198,7 +78405,7 @@ struct ReusableSpace {
static void *allocSpace(
struct ReusableSpace *p, /* Bulk memory available for allocation */
void *pBuf, /* Pointer to a prior allocation */
- int nByte /* Bytes of memory needed */
+ sqlite3_int64 nByte /* Bytes of memory needed */
){
assert( EIGHT_BYTE_ALIGNMENT(p->pSpace) );
if( pBuf==0 ){
@@ -81155,7 +81362,7 @@ static SQLITE_NOINLINE void invokeProfileCallback(sqlite3 *db, Vdbe *p){
assert( p->zSql!=0 );
sqlite3OsCurrentTimeInt64(db->pVfs, &iNow);
iElapse = (iNow - p->startTime)*1000000;
-#ifndef SQLITE_OMIT_DEPRECATED
+#ifndef SQLITE_OMIT_DEPRECATED
if( db->xProfile ){
db->xProfile(db->pProfileArg, p->zSql, iElapse);
}
@@ -81363,6 +81570,11 @@ SQLITE_API int sqlite3_value_nochange(sqlite3_value *pVal){
return (pVal->flags&(MEM_Null|MEM_Zero))==(MEM_Null|MEM_Zero);
}
+/* Return true if a parameter value originated from an sqlite3_bind() */
+SQLITE_API int sqlite3_value_frombind(sqlite3_value *pVal){
+ return (pVal->flags&MEM_FromBind)!=0;
+}
+
/* Make a copy of an sqlite3_value object
*/
SQLITE_API sqlite3_value *sqlite3_value_dup(const sqlite3_value *pOrig){
@@ -82208,10 +82420,10 @@ SQLITE_API int sqlite3_column_type(sqlite3_stmt *pStmt, int i){
** or a constant) then useTypes 2, 3, and 4 return NULL.
*/
static const void *columnName(
- sqlite3_stmt *pStmt,
- int N,
- const void *(*xFunc)(Mem*),
- int useType
+ sqlite3_stmt *pStmt, /* The statement */
+ int N, /* Which column to get the name for */
+ int useUtf16, /* True to return the name as UTF16 */
+ int useType /* What type of name */
){
const void *ret;
Vdbe *p;
@@ -82232,8 +82444,15 @@ static const void *columnName(
N += useType*n;
sqlite3_mutex_enter(db->mutex);
assert( db->mallocFailed==0 );
- ret = xFunc(&p->aColName[N]);
- /* A malloc may have failed inside of the xFunc() call. If this
+#ifndef SQLITE_OMIT_UTF16
+ if( useUtf16 ){
+ ret = sqlite3_value_text16((sqlite3_value*)&p->aColName[N]);
+ }else
+#endif
+ {
+ ret = sqlite3_value_text((sqlite3_value*)&p->aColName[N]);
+ }
+ /* A malloc may have failed inside of the _text() call. If this
** is the case, clear the mallocFailed flag and return NULL.
*/
if( db->mallocFailed ){
@@ -82250,13 +82469,11 @@ static const void *columnName(
** statement pStmt.
*/
SQLITE_API const char *sqlite3_column_name(sqlite3_stmt *pStmt, int N){
- return columnName(
- pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_NAME);
+ return columnName(pStmt, N, 0, COLNAME_NAME);
}
#ifndef SQLITE_OMIT_UTF16
SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt *pStmt, int N){
- return columnName(
- pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_NAME);
+ return columnName(pStmt, N, 1, COLNAME_NAME);
}
#endif
@@ -82275,13 +82492,11 @@ SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt *pStmt, int N){
** of the result set of SQL statement pStmt.
*/
SQLITE_API const char *sqlite3_column_decltype(sqlite3_stmt *pStmt, int N){
- return columnName(
- pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_DECLTYPE);
+ return columnName(pStmt, N, 0, COLNAME_DECLTYPE);
}
#ifndef SQLITE_OMIT_UTF16
SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt *pStmt, int N){
- return columnName(
- pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_DECLTYPE);
+ return columnName(pStmt, N, 1, COLNAME_DECLTYPE);
}
#endif /* SQLITE_OMIT_UTF16 */
#endif /* SQLITE_OMIT_DECLTYPE */
@@ -82293,13 +82508,11 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt *pStmt, int N){
** anything else which is not an unambiguous reference to a database column.
*/
SQLITE_API const char *sqlite3_column_database_name(sqlite3_stmt *pStmt, int N){
- return columnName(
- pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_DATABASE);
+ return columnName(pStmt, N, 0, COLNAME_DATABASE);
}
#ifndef SQLITE_OMIT_UTF16
SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt *pStmt, int N){
- return columnName(
- pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_DATABASE);
+ return columnName(pStmt, N, 1, COLNAME_DATABASE);
}
#endif /* SQLITE_OMIT_UTF16 */
@@ -82309,13 +82522,11 @@ SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt *pStmt, int N
** anything else which is not an unambiguous reference to a database column.
*/
SQLITE_API const char *sqlite3_column_table_name(sqlite3_stmt *pStmt, int N){
- return columnName(
- pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_TABLE);
+ return columnName(pStmt, N, 0, COLNAME_TABLE);
}
#ifndef SQLITE_OMIT_UTF16
SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt *pStmt, int N){
- return columnName(
- pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_TABLE);
+ return columnName(pStmt, N, 1, COLNAME_TABLE);
}
#endif /* SQLITE_OMIT_UTF16 */
@@ -82325,13 +82536,11 @@ SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt *pStmt, int N){
** anything else which is not an unambiguous reference to a database column.
*/
SQLITE_API const char *sqlite3_column_origin_name(sqlite3_stmt *pStmt, int N){
- return columnName(
- pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_COLUMN);
+ return columnName(pStmt, N, 0, COLNAME_COLUMN);
}
#ifndef SQLITE_OMIT_UTF16
SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt *pStmt, int N){
- return columnName(
- pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_COLUMN);
+ return columnName(pStmt, N, 1, COLNAME_COLUMN);
}
#endif /* SQLITE_OMIT_UTF16 */
#endif /* SQLITE_ENABLE_COLUMN_METADATA */
@@ -82700,6 +82909,14 @@ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt){
}
/*
+** Return 1 if the statement is an EXPLAIN and return 2 if the
+** statement is an EXPLAIN QUERY PLAN
+*/
+SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt){
+ return pStmt ? ((Vdbe*)pStmt)->explain : 0;
+}
+
+/*
** Return true if the prepared statement is in need of being reset.
*/
SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt *pStmt){
@@ -83388,12 +83605,20 @@ SQLITE_API int sqlite3_found_count = 0;
** feature is used for test suite validation only and does not appear an
** production builds.
**
-** M is an integer between 2 and 4. 2 indicates a ordinary two-way
-** branch (I=0 means fall through and I=1 means taken). 3 indicates
-** a 3-way branch where the third way is when one of the operands is
-** NULL. 4 indicates the OP_Jump instruction which has three destinations
-** depending on whether the first operand is less than, equal to, or greater
-** than the second.
+** M is the type of branch. I is the direction taken for this instance of
+** the branch.
+**
+** M: 2 - two-way branch (I=0: fall-thru 1: jump )
+** 3 - two-way + NULL (I=0: fall-thru 1: jump 2: NULL )
+** 4 - OP_Jump (I=0: jump p1 1: jump p2 2: jump p3)
+**
+** In other words, if M is 2, then I is either 0 (for fall-through) or
+** 1 (for when the branch is taken). If M is 3, the I is 0 for an
+** ordinary fall-through, I is 1 if the branch was taken, and I is 2
+** if the result of comparison is NULL. For M=3, I=2 the jump may or
+** may not be taken, depending on the SQLITE_JUMPIFNULL flags in p5.
+** When M is 4, that means that an OP_Jump is being run. I is 0, 1, or 2
+** depending on if the operands are less than, equal, or greater than.
**
** iSrcLine is the source code line (from the __LINE__ macro) that
** generated the VDBE instruction combined with flag bits. The source
@@ -83404,9 +83629,9 @@ SQLITE_API int sqlite3_found_count = 0;
** alternate branch are never taken. If a branch is never taken then
** flags should be 0x06 since only the fall-through approach is allowed.
**
-** Bit 0x04 of the flags indicates an OP_Jump opcode that is only
+** Bit 0x08 of the flags indicates an OP_Jump opcode that is only
** interested in equal or not-equal. In other words, I==0 and I==2
-** should be treated the same.
+** should be treated as equivalent
**
** Since only a line number is retained, not the filename, this macro
** only works for amalgamation builds. But that is ok, since these macros
@@ -83430,6 +83655,18 @@ SQLITE_API int sqlite3_found_count = 0;
mNever = iSrcLine >> 24;
assert( (I & mNever)==0 );
if( sqlite3GlobalConfig.xVdbeBranch==0 ) return; /*NO_TEST*/
+ /* Invoke the branch coverage callback with three arguments:
+ ** iSrcLine - the line number of the VdbeCoverage() macro, with
+ ** flags removed.
+ ** I - Mask of bits 0x07 indicating which cases are are
+ ** fulfilled by this instance of the jump. 0x01 means
+ ** fall-thru, 0x02 means taken, 0x04 means NULL. Any
+ ** impossible cases (ex: if the comparison is never NULL)
+ ** are filled in automatically so that the coverage
+ ** measurement logic does not flag those impossible cases
+ ** as missed coverage.
+ ** M - Type of jump. Same as M argument above
+ */
I |= mNever;
if( M==2 ) I |= 0x04;
if( M==4 ){
@@ -83977,6 +84214,15 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */
sqlite3VdbeEnter(p);
+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
+ if( db->xProgress ){
+ u32 iPrior = p->aCounter[SQLITE_STMTSTATUS_VM_STEP];
+ assert( 0 < db->nProgressOps );
+ nProgressLimit = db->nProgressOps - (iPrior % db->nProgressOps);
+ }else{
+ nProgressLimit = 0xffffffff;
+ }
+#endif
if( p->rc==SQLITE_NOMEM ){
/* This happens if a malloc() inside a call to sqlite3_column_text() or
** sqlite3_column_text16() failed. */
@@ -83990,15 +84236,6 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
db->busyHandler.nBusy = 0;
if( db->u1.isInterrupted ) goto abort_due_to_interrupt;
sqlite3VdbeIOTraceSql(p);
-#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
- if( db->xProgress ){
- u32 iPrior = p->aCounter[SQLITE_STMTSTATUS_VM_STEP];
- assert( 0 < db->nProgressOps );
- nProgressLimit = db->nProgressOps - (iPrior % db->nProgressOps);
- }else{
- nProgressLimit = 0xffffffff;
- }
-#endif
#ifdef SQLITE_DEBUG
sqlite3BeginBenignMalloc();
if( p->pc==0
@@ -84174,10 +84411,11 @@ check_for_interrupt:
** If the progress callback returns non-zero, exit the virtual machine with
** a return code SQLITE_ABORT.
*/
- if( nVmStep>=nProgressLimit && db->xProgress!=0 ){
+ while( nVmStep>=nProgressLimit && db->xProgress!=0 ){
assert( db->nProgressOps!=0 );
- nProgressLimit = nVmStep + db->nProgressOps - (nVmStep%db->nProgressOps);
+ nProgressLimit += db->nProgressOps;
if( db->xProgress(db->pProgressArg) ){
+ nProgressLimit = 0xffffffff;
rc = SQLITE_INTERRUPT;
goto abort_due_to_error;
}
@@ -84456,6 +84694,7 @@ case OP_String8: { /* same as TK_STRING, out2 */
if( encoding!=SQLITE_UTF8 ){
rc = sqlite3VdbeMemSetStr(pOut, pOp->p4.z, -1, SQLITE_UTF8, SQLITE_STATIC);
assert( rc==SQLITE_OK || rc==SQLITE_TOOBIG );
+ if( rc ) goto too_big;
if( SQLITE_OK!=sqlite3VdbeChangeEncoding(pOut, encoding) ) goto no_mem;
assert( pOut->szMalloc>0 && pOut->zMalloc==pOut->z );
assert( VdbeMemDynamic(pOut)==0 );
@@ -84468,7 +84707,6 @@ case OP_String8: { /* same as TK_STRING, out2 */
pOp->p4.z = pOut->z;
pOp->p1 = pOut->n;
}
- testcase( rc==SQLITE_TOOBIG );
#endif
if( pOp->p1>db->aLimit[SQLITE_LIMIT_LENGTH] ){
goto too_big;
@@ -84590,7 +84828,10 @@ case OP_Variable: { /* out2 */
goto too_big;
}
pOut = &aMem[pOp->p2];
- sqlite3VdbeMemShallowCopy(pOut, pVar, MEM_Static);
+ if( VdbeMemDynamic(pOut) ) sqlite3VdbeMemSetNull(pOut);
+ memcpy(pOut, pVar, MEMCELLSIZE);
+ pOut->flags &= ~(MEM_Dyn|MEM_Ephem);
+ pOut->flags |= MEM_Static|MEM_FromBind;
UPDATE_MAX_BLOBSIZE(pOut);
break;
}
@@ -84723,18 +84964,6 @@ case OP_ResultRow: {
assert( pOp->p1>0 );
assert( pOp->p1+pOp->p2<=(p->nMem+1 - p->nCursor)+1 );
-#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
- /* Run the progress counter just before returning.
- */
- if( db->xProgress!=0
- && nVmStep>=nProgressLimit
- && db->xProgress(db->pProgressArg)!=0
- ){
- rc = SQLITE_INTERRUPT;
- goto abort_due_to_error;
- }
-#endif
-
/* If this statement has violated immediate foreign key constraints, do
** not return the number of rows modified. And do not RELEASE the statement
** transaction. It needs to be rolled back. */
@@ -85100,8 +85329,8 @@ case OP_MustBeInt: { /* jump, in1 */
pIn1 = &aMem[pOp->p1];
if( (pIn1->flags & MEM_Int)==0 ){
applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding);
- VdbeBranchTaken((pIn1->flags&MEM_Int)==0, 2);
if( (pIn1->flags & MEM_Int)==0 ){
+ VdbeBranchTaken(1, 2);
if( pOp->p2==0 ){
rc = SQLITE_MISMATCH;
goto abort_due_to_error;
@@ -85110,6 +85339,7 @@ case OP_MustBeInt: { /* jump, in1 */
}
}
}
+ VdbeBranchTaken(0, 2);
MemSetTypeFlag(pIn1, MEM_Int);
break;
}
@@ -85284,7 +85514,6 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
** OP_Eq or OP_Ne) then take the jump or not depending on whether
** or not both operands are null.
*/
- assert( pOp->opcode==OP_Eq || pOp->opcode==OP_Ne );
assert( (flags1 & MEM_Cleared)==0 );
assert( (pOp->p5 & SQLITE_JUMPIFNULL)==0 || CORRUPT_DB );
testcase( (pOp->p5 & SQLITE_JUMPIFNULL)!=0 );
@@ -85293,7 +85522,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
){
res = 0; /* Operands are equal */
}else{
- res = 1; /* Operands are not equal */
+ res = ((flags3 & MEM_Null) ? -1 : +1); /* Operands are not equal */
}
}else{
/* SQLITE_NULLEQ is clear and at least one operand is NULL,
@@ -85411,7 +85640,7 @@ compare_op:
pOut->u.i = res2;
REGISTER_TRACE(pOp->p2, pOut);
}else{
- VdbeBranchTaken(res!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3);
+ VdbeBranchTaken(res2!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3);
if( res2 ){
goto jump_to_p2;
}
@@ -85961,15 +86190,15 @@ case OP_Column: {
zEndHdr = zData + aOffset[0];
testcase( zHdr>=zEndHdr );
do{
- if( (t = zHdr[0])<0x80 ){
+ if( (pC->aType[i] = t = zHdr[0])<0x80 ){
zHdr++;
offset64 += sqlite3VdbeOneByteSerialTypeLen(t);
}else{
zHdr += sqlite3GetVarint32(zHdr, &t);
+ pC->aType[i] = t;
offset64 += sqlite3VdbeSerialTypeLen(t);
}
- pC->aType[i++] = t;
- aOffset[i] = (u32)(offset64 & 0xffffffff);
+ aOffset[++i] = (u32)(offset64 & 0xffffffff);
}while( i<=p2 && zHdr<zEndHdr );
/* The record is corrupt if any of the following are true:
@@ -86972,6 +87201,7 @@ case OP_OpenDup: {
pCx->pKeyInfo = pOrig->pKeyInfo;
pCx->isTable = pOrig->isTable;
pCx->pgnoRoot = pOrig->pgnoRoot;
+ pCx->isOrdered = pOrig->isOrdered;
rc = sqlite3BtreeCursor(pOrig->pBtx, pCx->pgnoRoot, BTREE_WRCSR,
pCx->pKeyInfo, pCx->uc.pCursor);
/* The sqlite3BtreeCursor() routine can only fail for the first cursor
@@ -87935,14 +88165,7 @@ case OP_NewRowid: { /* out2 */
** This instruction only works on tables. The equivalent instruction
** for indices is OP_IdxInsert.
*/
-/* Opcode: InsertInt P1 P2 P3 P4 P5
-** Synopsis: intkey=P3 data=r[P2]
-**
-** This works exactly like OP_Insert except that the key is the
-** integer value P3, not the value of the integer stored in register P3.
-*/
-case OP_Insert:
-case OP_InsertInt: {
+case OP_Insert: {
Mem *pData; /* MEM cell holding data for the record to be inserted */
Mem *pKey; /* MEM cell holding key for the record */
VdbeCursor *pC; /* Cursor to table into which insert is written */
@@ -87963,16 +88186,11 @@ case OP_InsertInt: {
REGISTER_TRACE(pOp->p2, pData);
sqlite3VdbeIncrWriteCounter(p, pC);
- if( pOp->opcode==OP_Insert ){
- pKey = &aMem[pOp->p3];
- assert( pKey->flags & MEM_Int );
- assert( memIsValid(pKey) );
- REGISTER_TRACE(pOp->p3, pKey);
- x.nKey = pKey->u.i;
- }else{
- assert( pOp->opcode==OP_InsertInt );
- x.nKey = pOp->p3;
- }
+ pKey = &aMem[pOp->p3];
+ assert( pKey->flags & MEM_Int );
+ assert( memIsValid(pKey) );
+ REGISTER_TRACE(pOp->p3, pKey);
+ x.nKey = pKey->u.i;
if( pOp->p4type==P4_TABLE && HAS_UPDATE_HOOK(db) ){
assert( pC->iDb>=0 );
@@ -88492,7 +88710,7 @@ case OP_Sort: { /* jump */
p->aCounter[SQLITE_STMTSTATUS_SORT]++;
/* Fall through into OP_Rewind */
}
-/* Opcode: Rewind P1 P2 * * P5
+/* Opcode: Rewind P1 P2 * * *
**
** The next use of the Rowid or Column or Next instruction for P1
** will refer to the first entry in the database table or index.
@@ -88500,10 +88718,6 @@ case OP_Sort: { /* jump */
** If the table or index is not empty, fall through to the following
** instruction.
**
-** If P5 is non-zero and the table is not empty, then the "skip-next"
-** flag is set on the cursor so that the next OP_Next instruction
-** executed on it is a no-op.
-**
** This opcode leaves the cursor configured to move in forward order,
** from the beginning toward the end. In other words, the cursor is
** configured to use Next, not Prev.
@@ -88514,6 +88728,7 @@ case OP_Rewind: { /* jump */
int res;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ assert( pOp->p5==0 );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
assert( isSorter(pC)==(pOp->opcode==OP_SorterSort) );
@@ -88528,9 +88743,6 @@ case OP_Rewind: { /* jump */
pCrsr = pC->uc.pCursor;
assert( pCrsr );
rc = sqlite3BtreeFirst(pCrsr, &res);
-#ifndef SQLITE_OMIT_WINDOWFUNC
- if( pOp->p5 ) sqlite3BtreeSkipNext(pCrsr);
-#endif
pC->deferredMoveto = 0;
pC->cacheStatus = CACHE_STALE;
}
@@ -89540,8 +89752,7 @@ case OP_Program: { /* jump */
}
#endif
pOp = &aOp[-1];
-
- break;
+ goto check_for_interrupt;
}
/* Opcode: Param P1 P2 * * *
@@ -89913,6 +90124,7 @@ case OP_AggFinal: {
assert( (pMem->flags & ~(MEM_Null|MEM_Agg))==0 );
#ifndef SQLITE_OMIT_WINDOWFUNC
if( pOp->p3 ){
+ memAboutToChange(p, &aMem[pOp->p3]);
rc = sqlite3VdbeMemAggValue(pMem, &aMem[pOp->p3], pOp->p4.pFunc);
pMem = &aMem[pOp->p3];
}else
@@ -90950,7 +91162,16 @@ abort_due_to_error:
** release the mutexes on btrees that were acquired at the
** top. */
vdbe_return:
- testcase( nVmStep>0 );
+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
+ while( nVmStep>=nProgressLimit && db->xProgress!=0 ){
+ nProgressLimit += db->nProgressOps;
+ if( db->xProgress(db->pProgressArg) ){
+ nProgressLimit = 0xffffffff;
+ rc = SQLITE_INTERRUPT;
+ goto abort_due_to_error;
+ }
+ }
+#endif
p->aCounter[SQLITE_STMTSTATUS_VM_STEP] += (int)nVmStep;
sqlite3VdbeLeave(p);
assert( rc!=SQLITE_OK || nExtraDelete==0
@@ -92037,7 +92258,7 @@ static int vdbePmaReadBlob(
/* Extend the p->aAlloc[] allocation if required. */
if( p->nAlloc<nByte ){
u8 *aNew;
- int nNew = MAX(128, p->nAlloc*2);
+ sqlite3_int64 nNew = MAX(128, 2*(sqlite3_int64)p->nAlloc);
while( nByte>nNew ) nNew = nNew*2;
aNew = sqlite3Realloc(p->aAlloc, nNew);
if( !aNew ) return SQLITE_NOMEM_BKPT;
@@ -93328,15 +93549,19 @@ SQLITE_PRIVATE int sqlite3VdbeSorterWrite(
if( nMin>pSorter->nMemory ){
u8 *aNew;
- int iListOff = (u8*)pSorter->list.pList - pSorter->list.aMemory;
- int nNew = pSorter->nMemory * 2;
+ sqlite3_int64 nNew = 2 * (sqlite3_int64)pSorter->nMemory;
+ int iListOff = -1;
+ if( pSorter->list.pList ){
+ iListOff = (u8*)pSorter->list.pList - pSorter->list.aMemory;
+ }
while( nNew < nMin ) nNew = nNew*2;
if( nNew > pSorter->mxPmaSize ) nNew = pSorter->mxPmaSize;
if( nNew < nMin ) nNew = nMin;
-
aNew = sqlite3Realloc(pSorter->list.aMemory, nNew);
if( !aNew ) return SQLITE_NOMEM_BKPT;
- pSorter->list.pList = (SorterRecord*)&aNew[iListOff];
+ if( iListOff>=0 ){
+ pSorter->list.pList = (SorterRecord*)&aNew[iListOff];
+ }
pSorter->list.aMemory = aNew;
pSorter->nMemory = nNew;
}
@@ -95323,6 +95548,10 @@ static int lookupName(
sqlite3ErrorMsg(pParse, "misuse of aliased aggregate %s", zAs);
return WRC_Abort;
}
+ if( (pNC->ncFlags&NC_AllowWin)==0 && ExprHasProperty(pOrig, EP_Win) ){
+ sqlite3ErrorMsg(pParse, "misuse of aliased window function %s",zAs);
+ return WRC_Abort;
+ }
if( sqlite3ExprVectorSize(pOrig)!=1 ){
sqlite3ErrorMsg(pParse, "row value misused");
return WRC_Abort;
@@ -95613,6 +95842,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
const char *zId; /* The function name. */
FuncDef *pDef; /* Information about the function */
u8 enc = ENC(pParse->db); /* The database encoding */
+ int savedAllowFlags = (pNC->ncFlags & (NC_AllowAgg | NC_AllowWin));
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
zId = pExpr->u.zToken;
@@ -95734,8 +95964,11 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
pNC->nErr++;
}
if( is_agg ){
+ /* Window functions may not be arguments of aggregate functions.
+ ** Or arguments of other window functions. But aggregate functions
+ ** may be arguments for window functions. */
#ifndef SQLITE_OMIT_WINDOWFUNC
- pNC->ncFlags &= ~(pExpr->y.pWin ? NC_AllowWin : NC_AllowAgg);
+ pNC->ncFlags &= ~(NC_AllowWin | (!pExpr->y.pWin ? NC_AllowAgg : 0));
#else
pNC->ncFlags &= ~NC_AllowAgg;
#endif
@@ -95756,7 +95989,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
pExpr->y.pWin->pNextWin = pSel->pWin;
pSel->pWin = pExpr->y.pWin;
}
- pNC->ncFlags |= NC_AllowWin;
+ pNC->ncFlags |= NC_HasWin;
}else
#endif /* SQLITE_OMIT_WINDOWFUNC */
{
@@ -95774,8 +96007,8 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
pNC2->ncFlags |= NC_HasAgg | (pDef->funcFlags & SQLITE_FUNC_MINMAX);
}
- pNC->ncFlags |= NC_AllowAgg;
}
+ pNC->ncFlags |= savedAllowFlags;
}
/* FIX ME: Compute pExpr->affinity based on the expected return
** type of the function
@@ -96131,6 +96364,38 @@ SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(
return 0;
}
+#ifndef SQLITE_OMIT_WINDOWFUNC
+/*
+** Walker callback for resolveRemoveWindows().
+*/
+static int resolveRemoveWindowsCb(Walker *pWalker, Expr *pExpr){
+ if( ExprHasProperty(pExpr, EP_WinFunc) ){
+ Window **pp;
+ for(pp=&pWalker->u.pSelect->pWin; *pp; pp=&(*pp)->pNextWin){
+ if( *pp==pExpr->y.pWin ){
+ *pp = (*pp)->pNextWin;
+ break;
+ }
+ }
+ }
+ return WRC_Continue;
+}
+
+/*
+** Remove any Window objects owned by the expression pExpr from the
+** Select.pWin list of Select object pSelect.
+*/
+static void resolveRemoveWindows(Select *pSelect, Expr *pExpr){
+ Walker sWalker;
+ memset(&sWalker, 0, sizeof(Walker));
+ sWalker.xExprCallback = resolveRemoveWindowsCb;
+ sWalker.u.pSelect = pSelect;
+ sqlite3WalkExpr(&sWalker, pExpr);
+}
+#else
+# define resolveRemoveWindows(x,y)
+#endif
+
/*
** pOrderBy is an ORDER BY or GROUP BY clause in SELECT statement pSelect.
** The Name context of the SELECT statement is pNC. zType is either
@@ -96197,19 +96462,10 @@ static int resolveOrderGroupBy(
}
for(j=0; j<pSelect->pEList->nExpr; j++){
if( sqlite3ExprCompare(0, pE, pSelect->pEList->a[j].pExpr, -1)==0 ){
-#ifndef SQLITE_OMIT_WINDOWFUNC
- if( ExprHasProperty(pE, EP_WinFunc) ){
- /* Since this window function is being changed into a reference
- ** to the same window function the result set, remove the instance
- ** of this window function from the Select.pWin list. */
- Window **pp;
- for(pp=&pSelect->pWin; *pp; pp=&(*pp)->pNextWin){
- if( *pp==pE->y.pWin ){
- *pp = (*pp)->pNextWin;
- }
- }
- }
-#endif
+ /* Since this expresion is being changed into a reference
+ ** to an identical expression in the result set, remove all Window
+ ** objects belonging to the expression from the Select.pWin list. */
+ resolveRemoveWindows(pSelect, pE);
pItem->u.x.iOrderByCol = j+1;
}
}
@@ -96289,7 +96545,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
*/
for(i=0; i<p->pSrc->nSrc; i++){
struct SrcList_item *pItem = &p->pSrc->a[i];
- if( pItem->pSelect ){
+ if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){
NameContext *pNC; /* Used to iterate name contexts */
int nRef = 0; /* Refcount for pOuterNC and outer contexts */
const char *zSavedContext = pParse->zAuthContext;
@@ -96421,6 +96677,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
}
}
+#ifndef SQLITE_OMIT_WINDOWFUNC
if( IN_RENAME_OBJECT ){
Window *pWin;
for(pWin=p->pWinDefn; pWin; pWin=pWin->pNextWin){
@@ -96431,6 +96688,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
}
}
}
+#endif
/* If this is part of a compound SELECT, check that it has the right
** number of expressions in the select list. */
@@ -96511,8 +96769,8 @@ SQLITE_PRIVATE int sqlite3ResolveExprNames(
Walker w;
if( pExpr==0 ) return SQLITE_OK;
- savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg);
- pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg);
+ savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin);
+ pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin);
w.pParse = pNC->pParse;
w.xExprCallback = resolveExprStep;
w.xSelectCallback = resolveSelectStep;
@@ -96528,9 +96786,11 @@ SQLITE_PRIVATE int sqlite3ResolveExprNames(
#if SQLITE_MAX_EXPR_DEPTH>0
w.pParse->nHeight -= pExpr->nHeight;
#endif
- if( pNC->ncFlags & NC_HasAgg ){
- ExprSetProperty(pExpr, EP_Agg);
- }
+ assert( EP_Agg==NC_HasAgg );
+ assert( EP_Win==NC_HasWin );
+ testcase( pNC->ncFlags & NC_HasAgg );
+ testcase( pNC->ncFlags & NC_HasWin );
+ ExprSetProperty(pExpr, pNC->ncFlags & (NC_HasAgg|NC_HasWin) );
pNC->ncFlags |= savedHasAgg;
return pNC->nErr>0 || w.pParse->nErr>0;
}
@@ -97486,7 +97746,7 @@ SQLITE_PRIVATE Expr *sqlite3PExpr(
p = sqlite3DbMallocRawNN(pParse->db, sizeof(Expr));
if( p ){
memset(p, 0, sizeof(Expr));
- p->op = op & TKFLG_MASK;
+ p->op = op & 0xff;
p->iAgg = -1;
}
sqlite3ExprAttachSubtrees(pParse->db, p, pLeft, pRight);
@@ -97951,7 +98211,7 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){
static With *withDup(sqlite3 *db, With *p){
With *pRet = 0;
if( p ){
- int nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1);
+ sqlite3_int64 nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1);
pRet = sqlite3DbMallocZero(db, nByte);
if( pRet ){
int i;
@@ -98216,7 +98476,7 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListAppend(
}else if( (pList->nExpr & (pList->nExpr-1))==0 ){
ExprList *pNew;
pNew = sqlite3DbRealloc(db, pList,
- sizeof(*pList)+(2*pList->nExpr - 1)*sizeof(pList->a[0]));
+ sizeof(*pList)+(2*(sqlite3_int64)pList->nExpr-1)*sizeof(pList->a[0]));
if( pNew==0 ){
goto no_mem;
}
@@ -99181,14 +99441,11 @@ SQLITE_PRIVATE int sqlite3FindInIndex(
eType = IN_INDEX_EPH;
if( inFlags & IN_INDEX_LOOP ){
pParse->nQueryLoop = 0;
- if( pX->pLeft->iColumn<0 && !ExprHasProperty(pX, EP_xIsSelect) ){
- eType = IN_INDEX_ROWID;
- }
}else if( prRhsHasNull ){
*prRhsHasNull = rMayHaveNull = ++pParse->nMem;
}
assert( pX->op==TK_IN );
- sqlite3CodeRhsOfIN(pParse, pX, iTab, eType==IN_INDEX_ROWID);
+ sqlite3CodeRhsOfIN(pParse, pX, iTab);
if( rMayHaveNull ){
sqlite3SetHasNullFlag(v, iTab, rMayHaveNull);
}
@@ -99289,12 +99546,6 @@ SQLITE_PRIVATE void sqlite3VectorErrorMsg(Parse *pParse, Expr *pExpr){
** however the cursor number returned might not be the same, as it might
** have been duplicated using OP_OpenDup.
**
-** If parameter isRowid is non-zero, then LHS of the IN operator is guaranteed
-** to be a non-null integer. In this case, the ephemeral table can be an
-** table B-Tree that keyed by only integers. The more general cases uses
-** an index B-Tree which can have arbitrary keys, but is slower to both
-** read and write.
-**
** If the LHS expression ("x" in the examples) is a column value, or
** the SELECT statement returns a column value, then the affinity of that
** column is used to build the index keys. If both 'x' and the
@@ -99306,8 +99557,7 @@ SQLITE_PRIVATE void sqlite3VectorErrorMsg(Parse *pParse, Expr *pExpr){
SQLITE_PRIVATE void sqlite3CodeRhsOfIN(
Parse *pParse, /* Parsing context */
Expr *pExpr, /* The IN operator */
- int iTab, /* Use this cursor number */
- int isRowid /* If true, LHS is a rowid */
+ int iTab /* Use this cursor number */
){
int addrOnce = 0; /* Address of the OP_Once instruction at top */
int addr; /* Address of OP_OpenEphemeral instruction */
@@ -99360,14 +99610,12 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN(
/* Check to see if this is a vector IN operator */
pLeft = pExpr->pLeft;
nVal = sqlite3ExprVectorSize(pLeft);
- assert( !isRowid || nVal==1 );
/* Construct the ephemeral table that will contain the content of
** RHS of the IN operator.
*/
pExpr->iTable = iTab;
- addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral,
- pExpr->iTable, (isRowid?0:nVal));
+ addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, nVal);
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
VdbeComment((v, "Result of SELECT %u", pExpr->x.pSelect->selId));
@@ -99375,7 +99623,7 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN(
VdbeComment((v, "RHS of IN operator"));
}
#endif
- pKeyInfo = isRowid ? 0 : sqlite3KeyInfoAlloc(pParse->db, nVal, 1);
+ pKeyInfo = sqlite3KeyInfoAlloc(pParse->db, nVal, 1);
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
/* Case 1: expr IN (SELECT ...)
@@ -99389,7 +99637,6 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN(
ExplainQueryPlan((pParse, 1, "%sLIST SUBQUERY %d",
addrOnce?"":"CORRELATED ", pSelect->selId
));
- assert( !isRowid );
/* If the LHS and RHS of the IN operator do not match, that
** error will have been caught long before we reach this point. */
if( ALWAYS(pEList->nExpr==nVal) ){
@@ -99442,10 +99689,8 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN(
/* Loop through each expression in <exprlist>. */
r1 = sqlite3GetTempReg(pParse);
r2 = sqlite3GetTempReg(pParse);
- if( isRowid ) sqlite3VdbeAddOp4(v, OP_Blob, 0, r2, 0, "", P4_STATIC);
for(i=pList->nExpr, pItem=pList->a; i>0; i--, pItem++){
Expr *pE2 = pItem->pExpr;
- int iValToIns;
/* If the expression is not constant then we will need to
** disable the test that was generated above that makes sure
@@ -99458,20 +99703,9 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN(
}
/* Evaluate the expression and insert it into the temp table */
- if( isRowid && sqlite3ExprIsInteger(pE2, &iValToIns) ){
- sqlite3VdbeAddOp3(v, OP_InsertInt, iTab, r2, iValToIns);
- }else{
- r3 = sqlite3ExprCodeTarget(pParse, pE2, r1);
- if( isRowid ){
- sqlite3VdbeAddOp2(v, OP_MustBeInt, r3,
- sqlite3VdbeCurrentAddr(v)+2);
- VdbeCoverage(v);
- sqlite3VdbeAddOp3(v, OP_Insert, iTab, r2, r3);
- }else{
- sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1);
- sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iTab, r2, r3, 1);
- }
- }
+ r3 = sqlite3ExprCodeTarget(pParse, pE2, r1);
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1);
+ sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iTab, r2, r3, 1);
}
sqlite3ReleaseTempReg(pParse, r1);
sqlite3ReleaseTempReg(pParse, r2);
@@ -101687,6 +101921,17 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
*/
SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr *p, int iTab){
Walker w;
+ p = sqlite3ExprSkipCollate(p);
+ while( p ){
+ if( p->op==TK_NOTNULL ){
+ p = p->pLeft;
+ }else if( p->op==TK_AND ){
+ if( sqlite3ExprImpliesNonNullRow(p->pLeft, iTab) ) return 1;
+ p = p->pRight;
+ }else{
+ break;
+ }
+ }
w.xExprCallback = impliesNotNullRow;
w.xSelectCallback = 0;
w.xSelectCallback2 = 0;
@@ -102268,15 +102513,15 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable(
}
#endif
- /* Begin a transaction for database iDb.
- ** Then modify the schema cookie (since the ALTER TABLE modifies the
- ** schema). Open a statement transaction if the table is a virtual
- ** table.
- */
+ /* Begin a transaction for database iDb. Then modify the schema cookie
+ ** (since the ALTER TABLE modifies the schema). Call sqlite3MayAbort(),
+ ** as the scalar functions (e.g. sqlite_rename_table()) invoked by the
+ ** nested SQL may raise an exception. */
v = sqlite3GetVdbe(pParse);
if( v==0 ){
goto exit_rename_table;
}
+ sqlite3MayAbort(pParse);
/* figure out how many UTF-8 characters are in zName */
zTabName = pTab->zName;
@@ -102345,7 +102590,6 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable(
int i = ++pParse->nMem;
sqlite3VdbeLoadString(v, i, zName);
sqlite3VdbeAddOp4(v, OP_VRename, i, 0, 0,(const char*)pVTab, P4_VTAB);
- sqlite3MayAbort(pParse);
}
#endif
@@ -102666,6 +102910,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameColumn(
** uses the sqlite_rename_column() SQL function to compute the new
** CREATE statement text for the sqlite_master table.
*/
+ sqlite3MayAbort(pParse);
zNew = sqlite3NameFromToken(db, pNew);
if( !zNew ) goto exit_rename_column;
assert( pNew->n>0 );
@@ -105920,12 +106165,14 @@ static void attachFunc(
sqlite3BtreeEnterAll(db);
db->init.iDb = 0;
db->mDbFlags &= ~(DBFLAG_SchemaKnownOk);
- rc = sqlite3Init(db, &zErrDyn);
+ if( !REOPEN_AS_MEMDB(db) ){
+ rc = sqlite3Init(db, &zErrDyn);
+ }
sqlite3BtreeLeaveAll(db);
assert( zErrDyn==0 || rc!=SQLITE_OK );
}
#ifdef SQLITE_USER_AUTHENTICATION
- if( rc==SQLITE_OK ){
+ if( rc==SQLITE_OK && !REOPEN_AS_MEMDB(db) ){
u8 newAuth = 0;
rc = sqlite3UserAuthCheckLogin(db, zName, &newAuth);
if( newAuth<db->auth.authLevel ){
@@ -106854,7 +107101,12 @@ SQLITE_PRIVATE void sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){
zSql = sqlite3VMPrintf(db, zFormat, ap);
va_end(ap);
if( zSql==0 ){
- return; /* A malloc must have failed */
+ /* This can result either from an OOM or because the formatted string
+ ** exceeds SQLITE_LIMIT_LENGTH. In the latter case, we need to set
+ ** an error */
+ if( !db->mallocFailed ) pParse->rc = SQLITE_TOOBIG;
+ pParse->nErr++;
+ return;
}
pParse->nested++;
memcpy(saveBuf, PARSE_TAIL(pParse), PARSE_TAIL_SZ);
@@ -107994,7 +108246,8 @@ SQLITE_PRIVATE void sqlite3AddPrimaryKey(
&& sortOrder!=SQLITE_SO_DESC
){
if( IN_RENAME_OBJECT && pList ){
- sqlite3RenameTokenRemap(pParse, &pTab->iPKey, pList->a[0].pExpr);
+ Expr *pCExpr = sqlite3ExprSkipCollate(pList->a[0].pExpr);
+ sqlite3RenameTokenRemap(pParse, &pTab->iPKey, pCExpr);
}
pTab->iPKey = iCol;
pTab->keyConf = (u8)onError;
@@ -108415,6 +108668,7 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
pTab->iPKey = -1;
}else{
pPk = sqlite3PrimaryKeyIndex(pTab);
+ assert( pPk!=0 );
/*
** Remove all redundant columns from the PRIMARY KEY. For example, change
@@ -108584,6 +108838,11 @@ SQLITE_PRIVATE void sqlite3EndTable(
if( p->tnum==1 ) p->tabFlags |= TF_Readonly;
}
+ assert( (p->tabFlags & TF_HasPrimaryKey)==0
+ || p->iPKey>=0 || sqlite3PrimaryKeyIndex(p)!=0 );
+ assert( (p->tabFlags & TF_HasPrimaryKey)!=0
+ || (p->iPKey<0 && sqlite3PrimaryKeyIndex(p)==0) );
+
/* Special processing for WITHOUT ROWID Tables */
if( tabOpts & TF_WithoutRowid ){
if( (p->tabFlags & TF_Autoincrement) ){
@@ -109737,13 +109996,13 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
assert( pParse->nErr==0 );
if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0
&& db->init.busy==0
+ && pTblName!=0
#if SQLITE_USER_AUTHENTICATION
&& sqlite3UserAuthTable(pTab->zName)==0
#endif
#ifdef SQLITE_ALLOW_SQLITE_MASTER_INDEX
&& sqlite3StrICmp(&pTab->zName[7],"master")!=0
#endif
- && sqlite3StrNICmp(&pTab->zName[7],"altertab_",9)!=0
){
sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName);
goto exit_create_index;
@@ -109847,6 +110106,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
sqlite3ExprListSetSortOrder(pList, sortOrder);
}else{
sqlite3ExprListCheckLength(pParse, pList, "index");
+ if( pParse->nErr ) goto exit_create_index;
}
/* Figure out how many bytes of space are required to store explicitly
@@ -109865,6 +110125,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
*/
nName = sqlite3Strlen30(zName);
nExtraCol = pPk ? pPk->nKeyCol : 1;
+ assert( pList->nExpr + nExtraCol <= 32767 /* Fits in i16 */ );
pIndex = sqlite3AllocateIndexObject(db, pList->nExpr + nExtraCol,
nName + nExtra + 1, &zExtra);
if( db->mallocFailed ){
@@ -110348,9 +110609,9 @@ SQLITE_PRIVATE void *sqlite3ArrayAllocate(
int *pIdx /* Write the index of a new slot here */
){
char *z;
- int n = *pnEntry;
+ sqlite3_int64 n = *pIdx = *pnEntry;
if( (n & (n-1))==0 ){
- int sz = (n==0) ? 1 : 2*n;
+ sqlite3_int64 sz = (n==0) ? 1 : 2*n;
void *pNew = sqlite3DbRealloc(db, pArray, sz*szEntry);
if( pNew==0 ){
*pIdx = -1;
@@ -110360,7 +110621,6 @@ SQLITE_PRIVATE void *sqlite3ArrayAllocate(
}
z = (char*)pArray;
memset(&z[n * szEntry], 0, szEntry);
- *pIdx = n;
++*pnEntry;
return pArray;
}
@@ -110471,7 +110731,7 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge(
/* Allocate additional space if needed */
if( (u32)pSrc->nSrc+nExtra>pSrc->nAlloc ){
SrcList *pNew;
- int nAlloc = pSrc->nSrc*2+nExtra;
+ sqlite3_int64 nAlloc = 2*(sqlite3_int64)pSrc->nSrc+nExtra;
sqlite3 *db = pParse->db;
if( pSrc->nSrc+nExtra>=SQLITE_MAX_SRCLIST ){
@@ -110978,7 +111238,8 @@ SQLITE_PRIVATE void sqlite3UniqueConstraint(
StrAccum errMsg;
Table *pTab = pIdx->pTable;
- sqlite3StrAccumInit(&errMsg, pParse->db, 0, 0, 200);
+ sqlite3StrAccumInit(&errMsg, pParse->db, 0, 0,
+ pParse->db->aLimit[SQLITE_LIMIT_LENGTH]);
if( pIdx->aColExpr ){
sqlite3_str_appendf(&errMsg, "index '%q'", pIdx->zName);
}else{
@@ -111227,7 +111488,7 @@ SQLITE_PRIVATE With *sqlite3WithAdd(
}
if( pWith ){
- int nByte = sizeof(*pWith) + (sizeof(pWith->a[1]) * pWith->nCte);
+ sqlite3_int64 nByte = sizeof(*pWith) + (sizeof(pWith->a[1]) * pWith->nCte);
pNew = sqlite3DbRealloc(db, pWith, nByte);
}else{
pNew = sqlite3DbMallocZero(db, sizeof(*pWith));
@@ -114535,6 +114796,10 @@ static void setLikeOptFlag(sqlite3 *db, const char *zName, u8 flagVal){
if( ALWAYS(pDef) ){
pDef->funcFlags |= flagVal;
}
+ pDef = sqlite3FindFunction(db, zName, 3, SQLITE_UTF8, 0);
+ if( pDef ){
+ pDef->funcFlags |= flagVal;
+ }
}
/*
@@ -117857,7 +118122,9 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn, aRegIdx[ix]);
VdbeComment((v, "for %s", pIdx->zName));
#ifdef SQLITE_ENABLE_NULL_TRIM
- if( pIdx->idxType==2 ) sqlite3SetMakeRecordP5(v, pIdx->pTable);
+ if( pIdx->idxType==SQLITE_IDXTYPE_PRIMARYKEY ){
+ sqlite3SetMakeRecordP5(v, pIdx->pTable);
+ }
#endif
/* In an UPDATE operation, if this index is the PRIMARY KEY index
@@ -118107,10 +118374,13 @@ SQLITE_PRIVATE void sqlite3CompleteInsertion(
pik_flags |= (update_flags & OPFLAG_SAVEPOSITION);
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
if( update_flags==0 ){
- sqlite3VdbeAddOp4(v, OP_InsertInt,
- iIdxCur+i, aRegIdx[i], 0, (char*)pTab, P4_TABLE
+ int r = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, r);
+ sqlite3VdbeAddOp4(v, OP_Insert,
+ iIdxCur+i, aRegIdx[i], r, (char*)pTab, P4_TABLE
);
sqlite3VdbeChangeP5(v, OPFLAG_ISNOOP);
+ sqlite3ReleaseTempReg(pParse, r);
}
#endif
}
@@ -118458,6 +118728,13 @@ static int xferOptimization(
if( pSrcIdx==0 ){
return 0; /* pDestIdx has no corresponding index in pSrc */
}
+ if( pSrcIdx->tnum==pDestIdx->tnum && pSrc->pSchema==pDest->pSchema
+ && sqlite3FaultSim(411)==SQLITE_OK ){
+ /* The sqlite3FaultSim() call allows this corruption test to be
+ ** bypassed during testing, in order to exercise other corruption tests
+ ** further downstream. */
+ return 0; /* Corrupt schema - two indexes on the same btree */
+ }
}
#ifndef SQLITE_OMIT_CHECK
if( pDest->pCheck && sqlite3ExprListCompare(pSrc->pCheck,pDest->pCheck,-1) ){
@@ -118535,7 +118812,7 @@ static int xferOptimization(
sqlite3RowidConstraint(pParse, onError, pDest);
sqlite3VdbeJumpHere(v, addr2);
autoIncStep(pParse, regAutoinc, regRowid);
- }else if( pDest->pIndex==0 ){
+ }else if( pDest->pIndex==0 && !(db->mDbFlags & DBFLAG_VacuumInto) ){
addr1 = sqlite3VdbeAddOp2(v, OP_NewRowid, iDest, regRowid);
}else{
addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid);
@@ -118598,7 +118875,7 @@ static int xferOptimization(
sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest);
}
}
- if( !HasRowid(pSrc) && pDestIdx->idxType==2 ){
+ if( !HasRowid(pSrc) && pDestIdx->idxType==SQLITE_IDXTYPE_PRIMARYKEY ){
idxInsFlags |= OPFLAG_NCHANGE;
}
sqlite3VdbeAddOp2(v, OP_IdxInsert, iDest, regData);
@@ -119110,6 +119387,9 @@ struct sqlite3_api_routines {
void(*xDestroy)(void*));
/* Version 3.26.0 and later */
const char *(*normalized_sql)(sqlite3_stmt*);
+ /* Version 3.28.0 and later */
+ int (*stmt_isexplain)(sqlite3_stmt*);
+ int (*value_frombind)(sqlite3_value*);
};
/*
@@ -119399,6 +119679,9 @@ typedef int (*sqlite3_loadext_entry)(
#define sqlite3_create_window_function sqlite3_api->create_window_function
/* Version 3.26.0 and later */
#define sqlite3_normalized_sql sqlite3_api->normalized_sql
+/* Version 3.28.0 and later */
+#define sqlite3_stmt_isexplain sqlite3_api->isexplain
+#define sqlite3_value_frombind sqlite3_api->frombind
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
@@ -119858,10 +120141,13 @@ static const sqlite3_api_routines sqlite3Apis = {
sqlite3_create_window_function,
/* Version 3.26.0 and later */
#ifdef SQLITE_ENABLE_NORMALIZE
- sqlite3_normalized_sql
+ sqlite3_normalized_sql,
#else
- 0
+ 0,
#endif
+ /* Version 3.28.0 and later */
+ sqlite3_stmt_isexplain,
+ sqlite3_value_frombind
};
/*
@@ -129641,7 +129927,7 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){
**
** If regAcc is non-zero and there are no min() or max() aggregates
** in pAggInfo, then only populate the pAggInfo->nAccumulator accumulator
-** registers i register regAcc contains 0. The caller will take care
+** registers if register regAcc contains 0. The caller will take care
** of setting and clearing regAcc.
*/
static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){
@@ -133733,11 +134019,11 @@ build_vacuum_end:
/*
** This routine implements the OP_Vacuum opcode of the VDBE.
*/
-SQLITE_PRIVATE int sqlite3RunVacuum(
+SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum(
char **pzErrMsg, /* Write error message here */
sqlite3 *db, /* Database connection */
int iDb, /* Which attached DB to vacuum */
- sqlite3_value *pOut /* Write results here, if not NULL */
+ sqlite3_value *pOut /* Write results here, if not NULL. VACUUM INTO */
){
int rc = SQLITE_OK; /* Return code from service routines */
Btree *pMain; /* The database being vacuumed */
@@ -133746,6 +134032,7 @@ SQLITE_PRIVATE int sqlite3RunVacuum(
u64 saved_flags; /* Saved value of db->flags */
int saved_nChange; /* Saved value of db->nChange */
int saved_nTotalChange; /* Saved value of db->nTotalChange */
+ u32 saved_openFlags; /* Saved value of db->openFlags */
u8 saved_mTrace; /* Saved trace settings */
Db *pDb = 0; /* Database to detach at end of vacuum */
int isMemDb; /* True if vacuuming a :memory: database */
@@ -133756,18 +134043,21 @@ SQLITE_PRIVATE int sqlite3RunVacuum(
if( !db->autoCommit ){
sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction");
- return SQLITE_ERROR;
+ return SQLITE_ERROR; /* IMP: R-12218-18073 */
}
if( db->nVdbeActive>1 ){
sqlite3SetString(pzErrMsg, db,"cannot VACUUM - SQL statements in progress");
- return SQLITE_ERROR;
+ return SQLITE_ERROR; /* IMP: R-15610-35227 */
}
+ saved_openFlags = db->openFlags;
if( pOut ){
if( sqlite3_value_type(pOut)!=SQLITE_TEXT ){
sqlite3SetString(pzErrMsg, db, "non-text filename");
return SQLITE_ERROR;
}
zOut = (const char*)sqlite3_value_text(pOut);
+ db->openFlags &= ~SQLITE_OPEN_READONLY;
+ db->openFlags |= SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE;
}else{
zOut = "";
}
@@ -133806,6 +134096,7 @@ SQLITE_PRIVATE int sqlite3RunVacuum(
*/
nDb = db->nDb;
rc = execSqlF(db, pzErrMsg, "ATTACH %Q AS vacuum_db", zOut);
+ db->openFlags = saved_openFlags;
if( rc!=SQLITE_OK ) goto end_of_vacuum;
assert( (db->nDb-1)==nDb );
pDb = &db->aDb[nDb];
@@ -133819,6 +134110,7 @@ SQLITE_PRIVATE int sqlite3RunVacuum(
sqlite3SetString(pzErrMsg, db, "output file already exists");
goto end_of_vacuum;
}
+ db->mDbFlags |= DBFLAG_VacuumInto;
}
nRes = sqlite3BtreeGetOptimalReserve(pMain);
@@ -134307,9 +134599,13 @@ SQLITE_PRIVATE void sqlite3VtabClear(sqlite3 *db, Table *p){
** string will be freed automatically when the table is
** deleted.
*/
-static void addModuleArgument(sqlite3 *db, Table *pTable, char *zArg){
- int nBytes = sizeof(char *)*(2+pTable->nModuleArg);
+static void addModuleArgument(Parse *pParse, Table *pTable, char *zArg){
+ sqlite3_int64 nBytes = sizeof(char *)*(2+pTable->nModuleArg);
char **azModuleArg;
+ sqlite3 *db = pParse->db;
+ if( pTable->nModuleArg+3>=db->aLimit[SQLITE_LIMIT_COLUMN] ){
+ sqlite3ErrorMsg(pParse, "too many columns on %s", pTable->zName);
+ }
azModuleArg = sqlite3DbRealloc(db, pTable->azModuleArg, nBytes);
if( azModuleArg==0 ){
sqlite3DbFree(db, zArg);
@@ -134344,9 +134640,9 @@ SQLITE_PRIVATE void sqlite3VtabBeginParse(
db = pParse->db;
assert( pTable->nModuleArg==0 );
- addModuleArgument(db, pTable, sqlite3NameFromToken(db, pModuleName));
- addModuleArgument(db, pTable, 0);
- addModuleArgument(db, pTable, sqlite3DbStrDup(db, pTable->zName));
+ addModuleArgument(pParse, pTable, sqlite3NameFromToken(db, pModuleName));
+ addModuleArgument(pParse, pTable, 0);
+ addModuleArgument(pParse, pTable, sqlite3DbStrDup(db, pTable->zName));
assert( (pParse->sNameToken.z==pName2->z && pName2->z!=0)
|| (pParse->sNameToken.z==pName1->z && pName2->z==0)
);
@@ -134379,7 +134675,7 @@ static void addArgumentToVtab(Parse *pParse){
const char *z = (const char*)pParse->sArg.z;
int n = pParse->sArg.n;
sqlite3 *db = pParse->db;
- addModuleArgument(db, pParse->pNewTable, sqlite3DbStrNDup(db, z, n));
+ addModuleArgument(pParse, pParse->pNewTable, sqlite3DbStrNDup(db, z, n));
}
}
@@ -134668,7 +134964,8 @@ static int growVTrans(sqlite3 *db){
/* Grow the sqlite3.aVTrans array if required */
if( (db->nVTrans%ARRAY_INCR)==0 ){
VTable **aVTrans;
- int nBytes = sizeof(sqlite3_vtab *) * (db->nVTrans + ARRAY_INCR);
+ sqlite3_int64 nBytes = sizeof(sqlite3_vtab*)*
+ ((sqlite3_int64)db->nVTrans + ARRAY_INCR);
aVTrans = sqlite3DbRealloc(db, (void *)db->aVTrans, nBytes);
if( !aVTrans ){
return SQLITE_NOMEM_BKPT;
@@ -135164,9 +135461,9 @@ SQLITE_PRIVATE int sqlite3VtabEponymousTableInit(Parse *pParse, Module *pMod){
pTab->pSchema = db->aDb[0].pSchema;
assert( pTab->nModuleArg==0 );
pTab->iPKey = -1;
- addModuleArgument(db, pTab, sqlite3DbStrDup(db, pTab->zName));
- addModuleArgument(db, pTab, 0);
- addModuleArgument(db, pTab, sqlite3DbStrDup(db, pTab->zName));
+ addModuleArgument(pParse, pTab, sqlite3DbStrDup(db, pTab->zName));
+ addModuleArgument(pParse, pTab, 0);
+ addModuleArgument(pParse, pTab, sqlite3DbStrDup(db, pTab->zName));
rc = vtabCallConstructor(db, pTab, pMod, pModule->xConnect, &zErr);
if( rc ){
sqlite3ErrorMsg(pParse, "%s", zErr);
@@ -136427,7 +136724,6 @@ static int codeEqualityTerm(
if( pLoop->aLTerm[i]->pExpr==pX ){
int iOut = iReg + i - iEq;
if( eType==IN_INDEX_ROWID ){
- testcase( nEq>1 ); /* Happens with a UNIQUE index on ROWID */
pIn->addrInTop = sqlite3VdbeAddOp2(v, OP_Rowid, iTab, iOut);
}else{
int iCol = aiMap ? aiMap[iMap++] : 0;
@@ -137006,6 +137302,34 @@ static void whereIndexExprTrans(
}
/*
+** The pTruth expression is always true because it is the WHERE clause
+** a partial index that is driving a query loop. Look through all of the
+** WHERE clause terms on the query, and if any of those terms must be
+** true because pTruth is true, then mark those WHERE clause terms as
+** coded.
+*/
+static void whereApplyPartialIndexConstraints(
+ Expr *pTruth,
+ int iTabCur,
+ WhereClause *pWC
+){
+ int i;
+ WhereTerm *pTerm;
+ while( pTruth->op==TK_AND ){
+ whereApplyPartialIndexConstraints(pTruth->pLeft, iTabCur, pWC);
+ pTruth = pTruth->pRight;
+ }
+ for(i=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
+ Expr *pExpr;
+ if( pTerm->wtFlags & TERM_CODED ) continue;
+ pExpr = pTerm->pExpr;
+ if( sqlite3ExprCompare(0, pExpr, pTruth, iTabCur)==0 ){
+ pTerm->wtFlags |= TERM_CODED;
+ }
+ }
+}
+
+/*
** Generate code for the start of the iLevel-th loop in the WHERE clause
** implementation described by pWInfo.
*/
@@ -137189,6 +137513,9 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
sqlite3VdbeAddOp3(v, OP_SeekRowid, iCur, addrNxt, iRowidReg);
VdbeCoverage(v);
pLevel->op = OP_Noop;
+ if( (pTerm->prereqAll & pLevel->notReady)==0 ){
+ pTerm->wtFlags |= TERM_CODED;
+ }
}else if( (pLoop->wsFlags & WHERE_IPK)!=0
&& (pLoop->wsFlags & WHERE_COLUMN_RANGE)!=0
){
@@ -137611,6 +137938,14 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
whereIndexExprTrans(pIdx, iCur, iIdxCur, pWInfo);
}
+ /* If a partial index is driving the loop, try to eliminate WHERE clause
+ ** terms from the query that must be true due to the WHERE clause of
+ ** the partial index
+ */
+ if( pIdx->pPartIdxWhere ){
+ whereApplyPartialIndexConstraints(pIdx->pPartIdxWhere, iCur, pWC);
+ }
+
/* Record the instruction used to terminate the loop. */
if( pLoop->wsFlags & WHERE_ONEROW ){
pLevel->op = OP_Noop;
@@ -137774,7 +138109,12 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
pAndExpr = sqlite3ExprAnd(db, pAndExpr, pExpr);
}
if( pAndExpr ){
- pAndExpr = sqlite3PExpr(pParse, TK_AND|TKFLG_DONTFOLD, 0, pAndExpr);
+ /* The extra 0x10000 bit on the opcode is masked off and does not
+ ** become part of the new Expr.op. However, it does make the
+ ** op==TK_AND comparison inside of sqlite3PExpr() false, and this
+ ** prevents sqlite3PExpr() from implementing AND short-circuit
+ ** optimization, which we do not want here. */
+ pAndExpr = sqlite3PExpr(pParse, TK_AND|0x10000, 0, pAndExpr);
}
}
@@ -138004,8 +138344,9 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
u32 x = pLevel->iLikeRepCntr;
if( x>0 ){
skipLikeAddr = sqlite3VdbeAddOp1(v, (x&1)?OP_IfNot:OP_If,(int)(x>>1));
+ VdbeCoverageIf(v, (x&1)==1);
+ VdbeCoverageIf(v, (x&1)==0);
}
- VdbeCoverage(v);
#endif
}
#ifdef WHERETRACE_ENABLED /* 0xffff */
@@ -139607,6 +139948,12 @@ SQLITE_PRIVATE Bitmask sqlite3WhereExprUsageNN(WhereMaskSet *pMaskSet, Expr *p){
}else if( p->x.pList ){
mask |= sqlite3WhereExprListUsage(pMaskSet, p->x.pList);
}
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ if( p->op==TK_FUNCTION && p->y.pWin ){
+ mask |= sqlite3WhereExprListUsage(pMaskSet, p->y.pWin->pPartition);
+ mask |= sqlite3WhereExprListUsage(pMaskSet, p->y.pWin->pOrderBy);
+ }
+#endif
return mask;
}
SQLITE_PRIVATE Bitmask sqlite3WhereExprUsage(WhereMaskSet *pMaskSet, Expr *p){
@@ -143023,11 +143370,11 @@ static int whereLoopAddVirtual(
rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn);
/* If the call to xBestIndex() with all terms enabled produced a plan
- ** that does not require any source tables (IOW: a plan with mBest==0),
- ** then there is no point in making any further calls to xBestIndex()
- ** since they will all return the same result (if the xBestIndex()
- ** implementation is sane). */
- if( rc==SQLITE_OK && (mBest = (pNew->prereq & ~mPrereq))!=0 ){
+ ** that does not require any source tables (IOW: a plan with mBest==0)
+ ** and does not use an IN(...) operator, then there is no point in making
+ ** any further calls to xBestIndex() since they will all return the same
+ ** result (if the xBestIndex() implementation is sane). */
+ if( rc==SQLITE_OK && ((mBest = (pNew->prereq & ~mPrereq))!=0 || bIn) ){
int seenZero = 0; /* True if a plan with no prereqs seen */
int seenZeroNoIN = 0; /* Plan with no prereqs and no IN(...) seen */
Bitmask mPrev = 0;
@@ -145261,6 +145608,96 @@ static void dense_rankValueFunc(sqlite3_context *pCtx){
}
/*
+** Implementation of built-in window function nth_value(). This
+** implementation is used in "slow mode" only - when the EXCLUDE clause
+** is not set to the default value "NO OTHERS".
+*/
+struct NthValueCtx {
+ i64 nStep;
+ sqlite3_value *pValue;
+};
+static void nth_valueStepFunc(
+ sqlite3_context *pCtx,
+ int nArg,
+ sqlite3_value **apArg
+){
+ struct NthValueCtx *p;
+ p = (struct NthValueCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p));
+ if( p ){
+ i64 iVal;
+ switch( sqlite3_value_numeric_type(apArg[1]) ){
+ case SQLITE_INTEGER:
+ iVal = sqlite3_value_int64(apArg[1]);
+ break;
+ case SQLITE_FLOAT: {
+ double fVal = sqlite3_value_double(apArg[1]);
+ if( ((i64)fVal)!=fVal ) goto error_out;
+ iVal = (i64)fVal;
+ break;
+ }
+ default:
+ goto error_out;
+ }
+ if( iVal<=0 ) goto error_out;
+
+ p->nStep++;
+ if( iVal==p->nStep ){
+ p->pValue = sqlite3_value_dup(apArg[0]);
+ if( !p->pValue ){
+ sqlite3_result_error_nomem(pCtx);
+ }
+ }
+ }
+ UNUSED_PARAMETER(nArg);
+ UNUSED_PARAMETER(apArg);
+ return;
+
+ error_out:
+ sqlite3_result_error(
+ pCtx, "second argument to nth_value must be a positive integer", -1
+ );
+}
+static void nth_valueFinalizeFunc(sqlite3_context *pCtx){
+ struct NthValueCtx *p;
+ p = (struct NthValueCtx*)sqlite3_aggregate_context(pCtx, 0);
+ if( p && p->pValue ){
+ sqlite3_result_value(pCtx, p->pValue);
+ sqlite3_value_free(p->pValue);
+ p->pValue = 0;
+ }
+}
+#define nth_valueInvFunc noopStepFunc
+#define nth_valueValueFunc noopValueFunc
+
+static void first_valueStepFunc(
+ sqlite3_context *pCtx,
+ int nArg,
+ sqlite3_value **apArg
+){
+ struct NthValueCtx *p;
+ p = (struct NthValueCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p));
+ if( p && p->pValue==0 ){
+ p->pValue = sqlite3_value_dup(apArg[0]);
+ if( !p->pValue ){
+ sqlite3_result_error_nomem(pCtx);
+ }
+ }
+ UNUSED_PARAMETER(nArg);
+ UNUSED_PARAMETER(apArg);
+}
+static void first_valueFinalizeFunc(sqlite3_context *pCtx){
+ struct NthValueCtx *p;
+ p = (struct NthValueCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p));
+ if( p && p->pValue ){
+ sqlite3_result_value(pCtx, p->pValue);
+ sqlite3_value_free(p->pValue);
+ p->pValue = 0;
+ }
+}
+#define first_valueInvFunc noopStepFunc
+#define first_valueValueFunc noopValueFunc
+
+/*
** Implementation of built-in window function rank(). Assumes that
** the window frame has been set to:
**
@@ -145295,7 +145732,7 @@ static void rankValueFunc(sqlite3_context *pCtx){
** Implementation of built-in window function percent_rank(). Assumes that
** the window frame has been set to:
**
-** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
+** GROUPS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
*/
static void percent_rankStepFunc(
sqlite3_context *pCtx,
@@ -145303,38 +145740,44 @@ static void percent_rankStepFunc(
sqlite3_value **apArg
){
struct CallCount *p;
- UNUSED_PARAMETER(nArg); assert( nArg==1 );
-
+ UNUSED_PARAMETER(nArg); assert( nArg==0 );
+ UNUSED_PARAMETER(apArg);
p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p));
if( p ){
- if( p->nTotal==0 ){
- p->nTotal = sqlite3_value_int64(apArg[0]);
- }
- p->nStep++;
- if( p->nValue==0 ){
- p->nValue = p->nStep;
- }
+ p->nTotal++;
}
}
+static void percent_rankInvFunc(
+ sqlite3_context *pCtx,
+ int nArg,
+ sqlite3_value **apArg
+){
+ struct CallCount *p;
+ UNUSED_PARAMETER(nArg); assert( nArg==0 );
+ UNUSED_PARAMETER(apArg);
+ p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p));
+ p->nStep++;
+}
static void percent_rankValueFunc(sqlite3_context *pCtx){
struct CallCount *p;
p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p));
if( p ){
+ p->nValue = p->nStep;
if( p->nTotal>1 ){
- double r = (double)(p->nValue-1) / (double)(p->nTotal-1);
+ double r = (double)p->nValue / (double)(p->nTotal-1);
sqlite3_result_double(pCtx, r);
}else{
sqlite3_result_double(pCtx, 0.0);
}
- p->nValue = 0;
}
}
+#define percent_rankFinalizeFunc percent_rankValueFunc
/*
** Implementation of built-in window function cume_dist(). Assumes that
** the window frame has been set to:
**
-** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
+** GROUPS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING
*/
static void cume_distStepFunc(
sqlite3_context *pCtx,
@@ -145342,24 +145785,33 @@ static void cume_distStepFunc(
sqlite3_value **apArg
){
struct CallCount *p;
- assert( nArg==1 ); UNUSED_PARAMETER(nArg);
-
+ UNUSED_PARAMETER(nArg); assert( nArg==0 );
+ UNUSED_PARAMETER(apArg);
p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p));
if( p ){
- if( p->nTotal==0 ){
- p->nTotal = sqlite3_value_int64(apArg[0]);
- }
- p->nStep++;
+ p->nTotal++;
}
}
-static void cume_distValueFunc(sqlite3_context *pCtx){
+static void cume_distInvFunc(
+ sqlite3_context *pCtx,
+ int nArg,
+ sqlite3_value **apArg
+){
struct CallCount *p;
+ UNUSED_PARAMETER(nArg); assert( nArg==0 );
+ UNUSED_PARAMETER(apArg);
p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p));
- if( p && p->nTotal ){
+ p->nStep++;
+}
+static void cume_distValueFunc(sqlite3_context *pCtx){
+ struct CallCount *p;
+ p = (struct CallCount*)sqlite3_aggregate_context(pCtx, 0);
+ if( p ){
double r = (double)(p->nStep) / (double)(p->nTotal);
sqlite3_result_double(pCtx, r);
}
}
+#define cume_distFinalizeFunc cume_distValueFunc
/*
** Context object for ntile() window function.
@@ -145374,7 +145826,7 @@ struct NtileCtx {
** Implementation of ntile(). This assumes that the window frame has
** been coerced to:
**
-** ROWS UNBOUNDED PRECEDING AND CURRENT ROW
+** ROWS CURRENT ROW AND UNBOUNDED FOLLOWING
*/
static void ntileStepFunc(
sqlite3_context *pCtx,
@@ -145382,32 +145834,42 @@ static void ntileStepFunc(
sqlite3_value **apArg
){
struct NtileCtx *p;
- assert( nArg==2 ); UNUSED_PARAMETER(nArg);
+ assert( nArg==1 ); UNUSED_PARAMETER(nArg);
p = (struct NtileCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p));
if( p ){
if( p->nTotal==0 ){
p->nParam = sqlite3_value_int64(apArg[0]);
- p->nTotal = sqlite3_value_int64(apArg[1]);
if( p->nParam<=0 ){
sqlite3_result_error(
pCtx, "argument of ntile must be a positive integer", -1
);
}
}
- p->iRow++;
+ p->nTotal++;
}
}
+static void ntileInvFunc(
+ sqlite3_context *pCtx,
+ int nArg,
+ sqlite3_value **apArg
+){
+ struct NtileCtx *p;
+ assert( nArg==1 ); UNUSED_PARAMETER(nArg);
+ UNUSED_PARAMETER(apArg);
+ p = (struct NtileCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p));
+ p->iRow++;
+}
static void ntileValueFunc(sqlite3_context *pCtx){
struct NtileCtx *p;
p = (struct NtileCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p));
if( p && p->nParam>0 ){
int nSize = (p->nTotal / p->nParam);
if( nSize==0 ){
- sqlite3_result_int64(pCtx, p->iRow);
+ sqlite3_result_int64(pCtx, p->iRow+1);
}else{
i64 nLarge = p->nTotal - p->nParam*nSize;
i64 iSmall = nLarge*(nSize+1);
- i64 iRow = p->iRow-1;
+ i64 iRow = p->iRow;
assert( (nLarge*(nSize+1) + (p->nParam-nLarge)*nSize)==p->nTotal );
@@ -145419,6 +145881,7 @@ static void ntileValueFunc(sqlite3_context *pCtx){
}
}
}
+#define ntileFinalizeFunc ntileValueFunc
/*
** Context object for last_value() window function.
@@ -145468,7 +145931,7 @@ static void last_valueInvFunc(
}
static void last_valueValueFunc(sqlite3_context *pCtx){
struct LastValueCtx *p;
- p = (struct LastValueCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p));
+ p = (struct LastValueCtx*)sqlite3_aggregate_context(pCtx, 0);
if( p && p->pVal ){
sqlite3_result_value(pCtx, p->pVal);
}
@@ -145558,12 +146021,12 @@ SQLITE_PRIVATE void sqlite3WindowFunctions(void){
WINDOWFUNCX(row_number, 0, 0),
WINDOWFUNCX(dense_rank, 0, 0),
WINDOWFUNCX(rank, 0, 0),
- WINDOWFUNCX(percent_rank, 0, SQLITE_FUNC_WINDOW_SIZE),
- WINDOWFUNCX(cume_dist, 0, SQLITE_FUNC_WINDOW_SIZE),
- WINDOWFUNCX(ntile, 1, SQLITE_FUNC_WINDOW_SIZE),
+ WINDOWFUNCALL(percent_rank, 0, 0),
+ WINDOWFUNCALL(cume_dist, 0, 0),
+ WINDOWFUNCALL(ntile, 1, 0),
WINDOWFUNCALL(last_value, 1, 0),
- WINDOWFUNCNOOP(nth_value, 2, 0),
- WINDOWFUNCNOOP(first_value, 1, 0),
+ WINDOWFUNCALL(nth_value, 2, 0),
+ WINDOWFUNCALL(first_value, 1, 0),
WINDOWFUNCNOOP(lead, 1, 0),
WINDOWFUNCNOOP(lead, 2, 0),
WINDOWFUNCNOOP(lead, 3, 0),
@@ -145574,6 +146037,17 @@ SQLITE_PRIVATE void sqlite3WindowFunctions(void){
sqlite3InsertBuiltinFuncs(aWindowFuncs, ArraySize(aWindowFuncs));
}
+static Window *windowFind(Parse *pParse, Window *pList, const char *zName){
+ Window *p;
+ for(p=pList; p; p=p->pNextWin){
+ if( sqlite3StrICmp(p->zName, zName)==0 ) break;
+ }
+ if( p==0 ){
+ sqlite3ErrorMsg(pParse, "no such window: %s", zName);
+ }
+ return p;
+}
+
/*
** This function is called immediately after resolving the function name
** for a window function within a SELECT statement. Argument pList is a
@@ -145597,48 +146071,66 @@ SQLITE_PRIVATE void sqlite3WindowUpdate(
Window *pWin, /* Window frame to update */
FuncDef *pFunc /* Window function definition */
){
- if( pWin->zName && pWin->eType==0 ){
- Window *p;
- for(p=pList; p; p=p->pNextWin){
- if( sqlite3StrICmp(p->zName, pWin->zName)==0 ) break;
- }
- if( p==0 ){
- sqlite3ErrorMsg(pParse, "no such window: %s", pWin->zName);
- return;
- }
+ if( pWin->zName && pWin->eFrmType==0 ){
+ Window *p = windowFind(pParse, pList, pWin->zName);
+ if( p==0 ) return;
pWin->pPartition = sqlite3ExprListDup(pParse->db, p->pPartition, 0);
pWin->pOrderBy = sqlite3ExprListDup(pParse->db, p->pOrderBy, 0);
pWin->pStart = sqlite3ExprDup(pParse->db, p->pStart, 0);
pWin->pEnd = sqlite3ExprDup(pParse->db, p->pEnd, 0);
pWin->eStart = p->eStart;
pWin->eEnd = p->eEnd;
- pWin->eType = p->eType;
+ pWin->eFrmType = p->eFrmType;
+ pWin->eExclude = p->eExclude;
+ }else{
+ sqlite3WindowChain(pParse, pWin, pList);
}
+ if( (pWin->eFrmType==TK_RANGE)
+ && (pWin->pStart || pWin->pEnd)
+ && (pWin->pOrderBy==0 || pWin->pOrderBy->nExpr!=1)
+ ){
+ sqlite3ErrorMsg(pParse,
+ "RANGE with offset PRECEDING/FOLLOWING requires one ORDER BY expression"
+ );
+ }else
if( pFunc->funcFlags & SQLITE_FUNC_WINDOW ){
sqlite3 *db = pParse->db;
if( pWin->pFilter ){
sqlite3ErrorMsg(pParse,
"FILTER clause may only be used with aggregate window functions"
);
- }else
- if( pFunc->zName==row_numberName || pFunc->zName==ntileName ){
- sqlite3ExprDelete(db, pWin->pStart);
- sqlite3ExprDelete(db, pWin->pEnd);
- pWin->pStart = pWin->pEnd = 0;
- pWin->eType = TK_ROWS;
- pWin->eStart = TK_UNBOUNDED;
- pWin->eEnd = TK_CURRENT;
- }else
-
- if( pFunc->zName==dense_rankName || pFunc->zName==rankName
- || pFunc->zName==percent_rankName || pFunc->zName==cume_distName
- ){
- sqlite3ExprDelete(db, pWin->pStart);
- sqlite3ExprDelete(db, pWin->pEnd);
- pWin->pStart = pWin->pEnd = 0;
- pWin->eType = TK_RANGE;
- pWin->eStart = TK_UNBOUNDED;
- pWin->eEnd = TK_CURRENT;
+ }else{
+ struct WindowUpdate {
+ const char *zFunc;
+ int eFrmType;
+ int eStart;
+ int eEnd;
+ } aUp[] = {
+ { row_numberName, TK_ROWS, TK_UNBOUNDED, TK_CURRENT },
+ { dense_rankName, TK_RANGE, TK_UNBOUNDED, TK_CURRENT },
+ { rankName, TK_RANGE, TK_UNBOUNDED, TK_CURRENT },
+ { percent_rankName, TK_GROUPS, TK_CURRENT, TK_UNBOUNDED },
+ { cume_distName, TK_GROUPS, TK_FOLLOWING, TK_UNBOUNDED },
+ { ntileName, TK_ROWS, TK_CURRENT, TK_UNBOUNDED },
+ { leadName, TK_ROWS, TK_UNBOUNDED, TK_UNBOUNDED },
+ { lagName, TK_ROWS, TK_UNBOUNDED, TK_CURRENT },
+ };
+ int i;
+ for(i=0; i<ArraySize(aUp); i++){
+ if( pFunc->zName==aUp[i].zFunc ){
+ sqlite3ExprDelete(db, pWin->pStart);
+ sqlite3ExprDelete(db, pWin->pEnd);
+ pWin->pEnd = pWin->pStart = 0;
+ pWin->eFrmType = aUp[i].eFrmType;
+ pWin->eStart = aUp[i].eStart;
+ pWin->eEnd = aUp[i].eEnd;
+ pWin->eExclude = 0;
+ if( pWin->eStart==TK_FOLLOWING ){
+ pWin->pStart = sqlite3Expr(db, TK_INTEGER, "1");
+ }
+ break;
+ }
+ }
}
}
pWin->pFunc = pFunc;
@@ -145843,6 +146335,7 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){
** The OpenEphemeral instruction is coded later, after it is known how
** many columns the table will have. */
pMWin->iEphCsr = pParse->nTab++;
+ pParse->nTab += 3;
selectWindowRewriteEList(pParse, pMWin, pSrc, p->pEList, &pSublist);
selectWindowRewriteEList(pParse, pMWin, pSrc, p->pOrderBy, &pSublist);
@@ -145898,6 +146391,9 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){
}
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pMWin->iEphCsr, pSublist->nExpr);
+ sqlite3VdbeAddOp2(v, OP_OpenDup, pMWin->iEphCsr+1, pMWin->iEphCsr);
+ sqlite3VdbeAddOp2(v, OP_OpenDup, pMWin->iEphCsr+2, pMWin->iEphCsr);
+ sqlite3VdbeAddOp2(v, OP_OpenDup, pMWin->iEphCsr+3, pMWin->iEphCsr);
}else{
sqlite3SelectDelete(db, pSub);
}
@@ -145918,6 +146414,7 @@ SQLITE_PRIVATE void sqlite3WindowDelete(sqlite3 *db, Window *p){
sqlite3ExprDelete(db, p->pEnd);
sqlite3ExprDelete(db, p->pStart);
sqlite3DbFree(db, p->zName);
+ sqlite3DbFree(db, p->zBase);
sqlite3DbFree(db, p);
}
}
@@ -145954,16 +146451,18 @@ static Expr *sqlite3WindowOffsetExpr(Parse *pParse, Expr *pExpr){
*/
SQLITE_PRIVATE Window *sqlite3WindowAlloc(
Parse *pParse, /* Parsing context */
- int eType, /* Frame type. TK_RANGE or TK_ROWS */
+ int eType, /* Frame type. TK_RANGE, TK_ROWS, TK_GROUPS, or 0 */
int eStart, /* Start type: CURRENT, PRECEDING, FOLLOWING, UNBOUNDED */
Expr *pStart, /* Start window size if TK_PRECEDING or FOLLOWING */
int eEnd, /* End type: CURRENT, FOLLOWING, TK_UNBOUNDED, PRECEDING */
- Expr *pEnd /* End window size if TK_FOLLOWING or PRECEDING */
+ Expr *pEnd, /* End window size if TK_FOLLOWING or PRECEDING */
+ u8 eExclude /* EXCLUDE clause */
){
Window *pWin = 0;
+ int bImplicitFrame = 0;
/* Parser assures the following: */
- assert( eType==TK_RANGE || eType==TK_ROWS );
+ assert( eType==0 || eType==TK_RANGE || eType==TK_ROWS || eType==TK_GROUPS );
assert( eStart==TK_CURRENT || eStart==TK_PRECEDING
|| eStart==TK_UNBOUNDED || eStart==TK_FOLLOWING );
assert( eEnd==TK_CURRENT || eEnd==TK_FOLLOWING
@@ -145971,13 +146470,9 @@ SQLITE_PRIVATE Window *sqlite3WindowAlloc(
assert( (eStart==TK_PRECEDING || eStart==TK_FOLLOWING)==(pStart!=0) );
assert( (eEnd==TK_FOLLOWING || eEnd==TK_PRECEDING)==(pEnd!=0) );
-
- /* If a frame is declared "RANGE" (not "ROWS"), then it may not use
- ** either "<expr> PRECEDING" or "<expr> FOLLOWING".
- */
- if( eType==TK_RANGE && (pStart!=0 || pEnd!=0) ){
- sqlite3ErrorMsg(pParse, "RANGE must use only UNBOUNDED or CURRENT ROW");
- goto windowAllocErr;
+ if( eType==0 ){
+ bImplicitFrame = 1;
+ eType = TK_RANGE;
}
/* Additionally, the
@@ -145997,15 +146492,20 @@ SQLITE_PRIVATE Window *sqlite3WindowAlloc(
if( (eStart==TK_CURRENT && eEnd==TK_PRECEDING)
|| (eStart==TK_FOLLOWING && (eEnd==TK_PRECEDING || eEnd==TK_CURRENT))
){
- sqlite3ErrorMsg(pParse, "unsupported frame delimiter for ROWS");
+ sqlite3ErrorMsg(pParse, "unsupported frame specification");
goto windowAllocErr;
}
pWin = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
if( pWin==0 ) goto windowAllocErr;
- pWin->eType = eType;
+ pWin->eFrmType = eType;
pWin->eStart = eStart;
pWin->eEnd = eEnd;
+ if( eExclude==0 && OptimizationDisabled(pParse->db, SQLITE_WindowFunc) ){
+ eExclude = TK_NO;
+ }
+ pWin->eExclude = eExclude;
+ pWin->bImplicitFrame = bImplicitFrame;
pWin->pEnd = sqlite3WindowOffsetExpr(pParse, pEnd);
pWin->pStart = sqlite3WindowOffsetExpr(pParse, pStart);
return pWin;
@@ -146017,6 +146517,69 @@ windowAllocErr:
}
/*
+** Attach PARTITION and ORDER BY clauses pPartition and pOrderBy to window
+** pWin. Also, if parameter pBase is not NULL, set pWin->zBase to the
+** equivalent nul-terminated string.
+*/
+SQLITE_PRIVATE Window *sqlite3WindowAssemble(
+ Parse *pParse,
+ Window *pWin,
+ ExprList *pPartition,
+ ExprList *pOrderBy,
+ Token *pBase
+){
+ if( pWin ){
+ pWin->pPartition = pPartition;
+ pWin->pOrderBy = pOrderBy;
+ if( pBase ){
+ pWin->zBase = sqlite3DbStrNDup(pParse->db, pBase->z, pBase->n);
+ }
+ }else{
+ sqlite3ExprListDelete(pParse->db, pPartition);
+ sqlite3ExprListDelete(pParse->db, pOrderBy);
+ }
+ return pWin;
+}
+
+/*
+** Window *pWin has just been created from a WINDOW clause. Tokne pBase
+** is the base window. Earlier windows from the same WINDOW clause are
+** stored in the linked list starting at pWin->pNextWin. This function
+** either updates *pWin according to the base specification, or else
+** leaves an error in pParse.
+*/
+SQLITE_PRIVATE void sqlite3WindowChain(Parse *pParse, Window *pWin, Window *pList){
+ if( pWin->zBase ){
+ sqlite3 *db = pParse->db;
+ Window *pExist = windowFind(pParse, pList, pWin->zBase);
+ if( pExist ){
+ const char *zErr = 0;
+ /* Check for errors */
+ if( pWin->pPartition ){
+ zErr = "PARTITION clause";
+ }else if( pExist->pOrderBy && pWin->pOrderBy ){
+ zErr = "ORDER BY clause";
+ }else if( pExist->bImplicitFrame==0 ){
+ zErr = "frame specification";
+ }
+ if( zErr ){
+ sqlite3ErrorMsg(pParse,
+ "cannot override %s of window: %s", zErr, pWin->zBase
+ );
+ }else{
+ pWin->pPartition = sqlite3ExprListDup(db, pExist->pPartition, 0);
+ if( pExist->pOrderBy ){
+ assert( pWin->pOrderBy==0 );
+ pWin->pOrderBy = sqlite3ExprListDup(db, pExist->pOrderBy, 0);
+ }
+ sqlite3DbFree(db, pWin->zBase);
+ pWin->zBase = 0;
+ }
+ }
+ }
+}
+
+/*
** Attach window object pWin to expression p.
*/
SQLITE_PRIVATE void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){
@@ -146044,9 +146607,10 @@ SQLITE_PRIVATE void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){
** Identical window objects can be processed in a single scan.
*/
SQLITE_PRIVATE int sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2){
- if( p1->eType!=p2->eType ) return 1;
+ if( p1->eFrmType!=p2->eFrmType ) return 1;
if( p1->eStart!=p2->eStart ) return 1;
if( p1->eEnd!=p2->eEnd ) return 1;
+ if( p1->eExclude!=p2->eExclude ) return 1;
if( sqlite3ExprCompare(pParse, p1->pStart, p2->pStart, -1) ) return 1;
if( sqlite3ExprCompare(pParse, p1->pEnd, p2->pEnd, -1) ) return 1;
if( sqlite3ExprListCompare(p1->pPartition, p2->pPartition, -1) ) return 1;
@@ -146063,12 +146627,27 @@ SQLITE_PRIVATE int sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2){
SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse *pParse, Window *pMWin){
Window *pWin;
Vdbe *v = sqlite3GetVdbe(pParse);
- int nPart = (pMWin->pPartition ? pMWin->pPartition->nExpr : 0);
- nPart += (pMWin->pOrderBy ? pMWin->pOrderBy->nExpr : 0);
- if( nPart ){
+
+ /* Allocate registers to use for PARTITION BY values, if any. Initialize
+ ** said registers to NULL. */
+ if( pMWin->pPartition ){
+ int nExpr = pMWin->pPartition->nExpr;
pMWin->regPart = pParse->nMem+1;
- pParse->nMem += nPart;
- sqlite3VdbeAddOp3(v, OP_Null, 0, pMWin->regPart, pMWin->regPart+nPart-1);
+ pParse->nMem += nExpr;
+ sqlite3VdbeAddOp3(v, OP_Null, 0, pMWin->regPart, pMWin->regPart+nExpr-1);
+ }
+
+ pMWin->regOne = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, pMWin->regOne);
+
+ if( pMWin->eExclude ){
+ pMWin->regStartRowid = ++pParse->nMem;
+ pMWin->regEndRowid = ++pParse->nMem;
+ pMWin->csrApp = pParse->nTab++;
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, pMWin->regStartRowid);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, pMWin->regEndRowid);
+ sqlite3VdbeAddOp2(v, OP_OpenDup, pMWin->csrApp, pMWin->iEphCsr);
+ return;
}
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
@@ -146097,20 +146676,24 @@ SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse *pParse, Window *pMWin){
else if( p->zName==nth_valueName || p->zName==first_valueName ){
/* Allocate two registers at pWin->regApp. These will be used to
** store the start and end index of the current frame. */
- assert( pMWin->iEphCsr );
pWin->regApp = pParse->nMem+1;
pWin->csrApp = pParse->nTab++;
pParse->nMem += 2;
sqlite3VdbeAddOp2(v, OP_OpenDup, pWin->csrApp, pMWin->iEphCsr);
}
else if( p->zName==leadName || p->zName==lagName ){
- assert( pMWin->iEphCsr );
pWin->csrApp = pParse->nTab++;
sqlite3VdbeAddOp2(v, OP_OpenDup, pWin->csrApp, pMWin->iEphCsr);
}
}
}
+#define WINDOW_STARTING_INT 0
+#define WINDOW_ENDING_INT 1
+#define WINDOW_NTH_VALUE_INT 2
+#define WINDOW_STARTING_NUM 3
+#define WINDOW_ENDING_NUM 4
+
/*
** A "PRECEDING <expr>" (eCond==0) or "FOLLOWING <expr>" (eCond==1) or the
** value of the second argument to nth_value() (eCond==2) has just been
@@ -146118,25 +146701,42 @@ SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse *pParse, Window *pMWin){
** code to check that the value is a non-negative integer and throws an
** exception if it is not.
*/
-static void windowCheckIntValue(Parse *pParse, int reg, int eCond){
+static void windowCheckValue(Parse *pParse, int reg, int eCond){
static const char *azErr[] = {
"frame starting offset must be a non-negative integer",
"frame ending offset must be a non-negative integer",
- "second argument to nth_value must be a positive integer"
+ "second argument to nth_value must be a positive integer",
+ "frame starting offset must be a non-negative number",
+ "frame ending offset must be a non-negative number",
};
- static int aOp[] = { OP_Ge, OP_Ge, OP_Gt };
+ static int aOp[] = { OP_Ge, OP_Ge, OP_Gt, OP_Ge, OP_Ge };
Vdbe *v = sqlite3GetVdbe(pParse);
int regZero = sqlite3GetTempReg(pParse);
- assert( eCond==0 || eCond==1 || eCond==2 );
+ assert( eCond>=0 && eCond<ArraySize(azErr) );
sqlite3VdbeAddOp2(v, OP_Integer, 0, regZero);
- sqlite3VdbeAddOp2(v, OP_MustBeInt, reg, sqlite3VdbeCurrentAddr(v)+2);
- VdbeCoverageIf(v, eCond==0);
- VdbeCoverageIf(v, eCond==1);
- VdbeCoverageIf(v, eCond==2);
+ if( eCond>=WINDOW_STARTING_NUM ){
+ int regString = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, regString, 0, "", P4_STATIC);
+ sqlite3VdbeAddOp3(v, OP_Ge, regString, sqlite3VdbeCurrentAddr(v)+2, reg);
+ sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC|SQLITE_JUMPIFNULL);
+ VdbeCoverage(v);
+ assert( eCond==3 || eCond==4 );
+ VdbeCoverageIf(v, eCond==3);
+ VdbeCoverageIf(v, eCond==4);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_MustBeInt, reg, sqlite3VdbeCurrentAddr(v)+2);
+ VdbeCoverage(v);
+ assert( eCond==0 || eCond==1 || eCond==2 );
+ VdbeCoverageIf(v, eCond==0);
+ VdbeCoverageIf(v, eCond==1);
+ VdbeCoverageIf(v, eCond==2);
+ }
sqlite3VdbeAddOp3(v, aOp[eCond], regZero, sqlite3VdbeCurrentAddr(v)+2, reg);
- VdbeCoverageNeverNullIf(v, eCond==0);
- VdbeCoverageNeverNullIf(v, eCond==1);
+ VdbeCoverageNeverNullIf(v, eCond==0); /* NULL case captured by */
+ VdbeCoverageNeverNullIf(v, eCond==1); /* the OP_MustBeInt */
VdbeCoverageNeverNullIf(v, eCond==2);
+ VdbeCoverageNeverNullIf(v, eCond==3); /* NULL case caught by */
+ VdbeCoverageNeverNullIf(v, eCond==4); /* the OP_Ge */
sqlite3MayAbort(pParse);
sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_ERROR, OE_Abort);
sqlite3VdbeAppendP4(v, (void*)azErr[eCond], P4_STATIC);
@@ -146176,37 +146776,28 @@ static void windowAggStep(
Window *pMWin, /* Linked list of window functions */
int csr, /* Read arguments from this cursor */
int bInverse, /* True to invoke xInverse instead of xStep */
- int reg, /* Array of registers */
- int regPartSize /* Register containing size of partition */
+ int reg /* Array of registers */
){
Vdbe *v = sqlite3GetVdbe(pParse);
Window *pWin;
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
- int flags = pWin->pFunc->funcFlags;
+ FuncDef *pFunc = pWin->pFunc;
int regArg;
int nArg = windowArgCount(pWin);
+ int i;
- if( csr>=0 ){
- int i;
- for(i=0; i<nArg; i++){
+ for(i=0; i<nArg; i++){
+ if( i!=1 || pFunc->zName!=nth_valueName ){
sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+i, reg+i);
+ }else{
+ sqlite3VdbeAddOp3(v, OP_Column, pMWin->iEphCsr, pWin->iArgCol+i, reg+i);
}
- regArg = reg;
- if( flags & SQLITE_FUNC_WINDOW_SIZE ){
- if( nArg==0 ){
- regArg = regPartSize;
- }else{
- sqlite3VdbeAddOp2(v, OP_SCopy, regPartSize, reg+nArg);
- }
- nArg++;
- }
- }else{
- assert( !(flags & SQLITE_FUNC_WINDOW_SIZE) );
- regArg = reg + pWin->iArgCol;
}
+ regArg = reg;
- if( (pWin->pFunc->funcFlags & SQLITE_FUNC_MINMAX)
- && pWin->eStart!=TK_UNBOUNDED
+ if( pMWin->regStartRowid==0
+ && (pFunc->funcFlags & SQLITE_FUNC_MINMAX)
+ && (pWin->eStart!=TK_UNBOUNDED)
){
int addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regArg);
VdbeCoverage(v);
@@ -146223,34 +146814,24 @@ static void windowAggStep(
}
sqlite3VdbeJumpHere(v, addrIsNull);
}else if( pWin->regApp ){
- assert( pWin->pFunc->zName==nth_valueName
- || pWin->pFunc->zName==first_valueName
+ assert( pFunc->zName==nth_valueName
+ || pFunc->zName==first_valueName
);
assert( bInverse==0 || bInverse==1 );
sqlite3VdbeAddOp2(v, OP_AddImm, pWin->regApp+1-bInverse, 1);
- }else if( pWin->pFunc->zName==leadName
- || pWin->pFunc->zName==lagName
- ){
- /* no-op */
- }else{
+ }else if( pFunc->xSFunc!=noopStepFunc ){
int addrIf = 0;
if( pWin->pFilter ){
int regTmp;
assert( nArg==0 || nArg==pWin->pOwner->x.pList->nExpr );
assert( nArg || pWin->pOwner->x.pList==0 );
- if( csr>0 ){
- regTmp = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+nArg,regTmp);
- }else{
- regTmp = regArg + nArg;
- }
+ regTmp = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+nArg,regTmp);
addrIf = sqlite3VdbeAddOp3(v, OP_IfNot, regTmp, 0, 1);
VdbeCoverage(v);
- if( csr>0 ){
- sqlite3ReleaseTempReg(pParse, regTmp);
- }
+ sqlite3ReleaseTempReg(pParse, regTmp);
}
- if( pWin->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
+ if( pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
CollSeq *pColl;
assert( nArg>0 );
pColl = sqlite3ExprNNCollSeq(pParse, pWin->pOwner->x.pList->a[0].pExpr);
@@ -146258,45 +146839,96 @@ static void windowAggStep(
}
sqlite3VdbeAddOp3(v, bInverse? OP_AggInverse : OP_AggStep,
bInverse, regArg, pWin->regAccum);
- sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
+ sqlite3VdbeAppendP4(v, pFunc, P4_FUNCDEF);
sqlite3VdbeChangeP5(v, (u8)nArg);
if( addrIf ) sqlite3VdbeJumpHere(v, addrIf);
}
}
}
+typedef struct WindowCodeArg WindowCodeArg;
+typedef struct WindowCsrAndReg WindowCsrAndReg;
+struct WindowCsrAndReg {
+ int csr;
+ int reg;
+};
+
+struct WindowCodeArg {
+ Parse *pParse;
+ Window *pMWin;
+ Vdbe *pVdbe;
+ int regGosub;
+ int addrGosub;
+ int regArg;
+ int eDelete;
+
+ WindowCsrAndReg start;
+ WindowCsrAndReg current;
+ WindowCsrAndReg end;
+};
+
+/*
+** Values that may be passed as the second argument to windowCodeOp().
+*/
+#define WINDOW_RETURN_ROW 1
+#define WINDOW_AGGINVERSE 2
+#define WINDOW_AGGSTEP 3
+
+/*
+** Generate VM code to read the window frames peer values from cursor csr into
+** an array of registers starting at reg.
+*/
+static void windowReadPeerValues(
+ WindowCodeArg *p,
+ int csr,
+ int reg
+){
+ Window *pMWin = p->pMWin;
+ ExprList *pOrderBy = pMWin->pOrderBy;
+ if( pOrderBy ){
+ Vdbe *v = sqlite3GetVdbe(p->pParse);
+ ExprList *pPart = pMWin->pPartition;
+ int iColOff = pMWin->nBufferCol + (pPart ? pPart->nExpr : 0);
+ int i;
+ for(i=0; i<pOrderBy->nExpr; i++){
+ sqlite3VdbeAddOp3(v, OP_Column, csr, iColOff+i, reg+i);
+ }
+ }
+}
+
/*
-** Generate VM code to invoke either xValue() (bFinal==0) or xFinalize()
-** (bFinal==1) for each window function in the linked list starting at
+** Generate VM code to invoke either xValue() (bFin==0) or xFinalize()
+** (bFin==1) for each window function in the linked list starting at
** pMWin. Or, for built-in window-functions that do not use the standard
** API, generate the equivalent VM code.
*/
-static void windowAggFinal(Parse *pParse, Window *pMWin, int bFinal){
+static void windowAggFinal(WindowCodeArg *p, int bFin){
+ Parse *pParse = p->pParse;
+ Window *pMWin = p->pMWin;
Vdbe *v = sqlite3GetVdbe(pParse);
Window *pWin;
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
- if( (pWin->pFunc->funcFlags & SQLITE_FUNC_MINMAX)
- && pWin->eStart!=TK_UNBOUNDED
+ if( pMWin->regStartRowid==0
+ && (pWin->pFunc->funcFlags & SQLITE_FUNC_MINMAX)
+ && (pWin->eStart!=TK_UNBOUNDED)
){
sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult);
sqlite3VdbeAddOp1(v, OP_Last, pWin->csrApp);
VdbeCoverage(v);
sqlite3VdbeAddOp3(v, OP_Column, pWin->csrApp, 0, pWin->regResult);
sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2);
- if( bFinal ){
- sqlite3VdbeAddOp1(v, OP_ResetSorter, pWin->csrApp);
- }
}else if( pWin->regApp ){
+ assert( pMWin->regStartRowid==0 );
}else{
- if( bFinal ){
- sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, windowArgCount(pWin));
+ int nArg = windowArgCount(pWin);
+ if( bFin ){
+ sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, nArg);
sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
sqlite3VdbeAddOp2(v, OP_Copy, pWin->regAccum, pWin->regResult);
sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum);
}else{
- sqlite3VdbeAddOp3(v, OP_AggValue, pWin->regAccum, windowArgCount(pWin),
- pWin->regResult);
+ sqlite3VdbeAddOp3(v, OP_AggValue,pWin->regAccum,nArg,pWin->regResult);
sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
}
}
@@ -146304,66 +146936,97 @@ static void windowAggFinal(Parse *pParse, Window *pMWin, int bFinal){
}
/*
-** This function generates VM code to invoke the sub-routine at address
-** lblFlushPart once for each partition with the entire partition cached in
-** the Window.iEphCsr temp table.
+** Generate code to calculate the current values of all window functions in the
+** p->pMWin list by doing a full scan of the current window frame. Store the
+** results in the Window.regResult registers, ready to return the upper
+** layer.
*/
-static void windowPartitionCache(
- Parse *pParse,
- Select *p, /* The rewritten SELECT statement */
- WhereInfo *pWInfo, /* WhereInfo to call WhereEnd() on */
- int regFlushPart, /* Register to use with Gosub lblFlushPart */
- int lblFlushPart, /* Subroutine to Gosub to */
- int *pRegSize /* OUT: Register containing partition size */
-){
- Window *pMWin = p->pWin;
- Vdbe *v = sqlite3GetVdbe(pParse);
- int iSubCsr = p->pSrc->a[0].iCursor;
- int nSub = p->pSrc->a[0].pTab->nCol;
- int k;
+static void windowFullScan(WindowCodeArg *p){
+ Window *pWin;
+ Parse *pParse = p->pParse;
+ Window *pMWin = p->pMWin;
+ Vdbe *v = p->pVdbe;
- int reg = pParse->nMem+1;
- int regRecord = reg+nSub;
- int regRowid = regRecord+1;
+ int regCRowid = 0; /* Current rowid value */
+ int regCPeer = 0; /* Current peer values */
+ int regRowid = 0; /* AggStep rowid value */
+ int regPeer = 0; /* AggStep peer values */
- *pRegSize = regRowid;
- pParse->nMem += nSub + 2;
+ int nPeer;
+ int lblNext;
+ int lblBrk;
+ int addrNext;
+ int csr = pMWin->csrApp;
- /* Load the column values for the row returned by the sub-select
- ** into an array of registers starting at reg. */
- for(k=0; k<nSub; k++){
- sqlite3VdbeAddOp3(v, OP_Column, iSubCsr, k, reg+k);
+ nPeer = (pMWin->pOrderBy ? pMWin->pOrderBy->nExpr : 0);
+
+ lblNext = sqlite3VdbeMakeLabel(pParse);
+ lblBrk = sqlite3VdbeMakeLabel(pParse);
+
+ regCRowid = sqlite3GetTempReg(pParse);
+ regRowid = sqlite3GetTempReg(pParse);
+ if( nPeer ){
+ regCPeer = sqlite3GetTempRange(pParse, nPeer);
+ regPeer = sqlite3GetTempRange(pParse, nPeer);
}
- sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, nSub, regRecord);
- /* Check if this is the start of a new partition. If so, call the
- ** flush_partition sub-routine. */
- if( pMWin->pPartition ){
+ sqlite3VdbeAddOp2(v, OP_Rowid, pMWin->iEphCsr, regCRowid);
+ windowReadPeerValues(p, pMWin->iEphCsr, regCPeer);
+
+ for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
+ sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum);
+ }
+
+ sqlite3VdbeAddOp3(v, OP_SeekGE, csr, lblBrk, pMWin->regStartRowid);
+ VdbeCoverage(v);
+ addrNext = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeAddOp2(v, OP_Rowid, csr, regRowid);
+ sqlite3VdbeAddOp3(v, OP_Gt, pMWin->regEndRowid, lblBrk, regRowid);
+ VdbeCoverageNeverNull(v);
+
+ if( pMWin->eExclude==TK_CURRENT ){
+ sqlite3VdbeAddOp3(v, OP_Eq, regCRowid, lblNext, regRowid);
+ VdbeCoverageNeverNull(v);
+ }else if( pMWin->eExclude!=TK_NO ){
int addr;
- ExprList *pPart = pMWin->pPartition;
- int nPart = pPart->nExpr;
- int regNewPart = reg + pMWin->nBufferCol;
- KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pPart, 0, 0);
+ int addrEq = 0;
+ KeyInfo *pKeyInfo = 0;
- addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, pMWin->regPart,nPart);
- sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
- sqlite3VdbeAddOp3(v, OP_Jump, addr+2, addr+4, addr+2);
- VdbeCoverageEqNe(v);
- sqlite3VdbeAddOp3(v, OP_Copy, regNewPart, pMWin->regPart, nPart-1);
- sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, lblFlushPart);
- VdbeComment((v, "call flush_partition"));
+ if( pMWin->pOrderBy ){
+ pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pMWin->pOrderBy, 0, 0);
+ }
+ if( pMWin->eExclude==TK_TIES ){
+ addrEq = sqlite3VdbeAddOp3(v, OP_Eq, regCRowid, 0, regRowid);
+ VdbeCoverageNeverNull(v);
+ }
+ if( pKeyInfo ){
+ windowReadPeerValues(p, csr, regPeer);
+ sqlite3VdbeAddOp3(v, OP_Compare, regPeer, regCPeer, nPeer);
+ sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
+ addr = sqlite3VdbeCurrentAddr(v)+1;
+ sqlite3VdbeAddOp3(v, OP_Jump, addr, lblNext, addr);
+ VdbeCoverageEqNe(v);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, lblNext);
+ }
+ if( addrEq ) sqlite3VdbeJumpHere(v, addrEq);
}
- /* Buffer the current row in the ephemeral table. */
- sqlite3VdbeAddOp2(v, OP_NewRowid, pMWin->iEphCsr, regRowid);
- sqlite3VdbeAddOp3(v, OP_Insert, pMWin->iEphCsr, regRecord, regRowid);
+ windowAggStep(pParse, pMWin, csr, 0, p->regArg);
- /* End of the input loop */
- sqlite3WhereEnd(pWInfo);
+ sqlite3VdbeResolveLabel(v, lblNext);
+ sqlite3VdbeAddOp2(v, OP_Next, csr, addrNext);
+ VdbeCoverage(v);
+ sqlite3VdbeJumpHere(v, addrNext-1);
+ sqlite3VdbeJumpHere(v, addrNext+1);
+ sqlite3ReleaseTempReg(pParse, regRowid);
+ sqlite3ReleaseTempReg(pParse, regCRowid);
+ if( nPeer ){
+ sqlite3ReleaseTempRange(pParse, regPeer, nPeer);
+ sqlite3ReleaseTempRange(pParse, regCPeer, nPeer);
+ }
- /* Invoke "flush_partition" to deal with the final (or only) partition */
- sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, lblFlushPart);
- VdbeComment((v, "call flush_partition"));
+ windowAggFinal(p, 1);
}
/*
@@ -146379,110 +147042,74 @@ static void windowPartitionCache(
** lag()
** lead()
*/
-static void windowReturnOneRow(
- Parse *pParse,
- Window *pMWin,
- int regGosub,
- int addrGosub
-){
- Vdbe *v = sqlite3GetVdbe(pParse);
- Window *pWin;
- for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
- FuncDef *pFunc = pWin->pFunc;
- if( pFunc->zName==nth_valueName
- || pFunc->zName==first_valueName
- ){
- int csr = pWin->csrApp;
- int lbl = sqlite3VdbeMakeLabel(pParse);
- int tmpReg = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult);
+static void windowReturnOneRow(WindowCodeArg *p){
+ Window *pMWin = p->pMWin;
+ Vdbe *v = p->pVdbe;
- if( pFunc->zName==nth_valueName ){
- sqlite3VdbeAddOp3(v, OP_Column, pMWin->iEphCsr, pWin->iArgCol+1,tmpReg);
- windowCheckIntValue(pParse, tmpReg, 2);
- }else{
- sqlite3VdbeAddOp2(v, OP_Integer, 1, tmpReg);
- }
- sqlite3VdbeAddOp3(v, OP_Add, tmpReg, pWin->regApp, tmpReg);
- sqlite3VdbeAddOp3(v, OP_Gt, pWin->regApp+1, lbl, tmpReg);
- VdbeCoverageNeverNull(v);
- sqlite3VdbeAddOp3(v, OP_SeekRowid, csr, 0, tmpReg);
- VdbeCoverageNeverTaken(v);
- sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol, pWin->regResult);
- sqlite3VdbeResolveLabel(v, lbl);
- sqlite3ReleaseTempReg(pParse, tmpReg);
- }
- else if( pFunc->zName==leadName || pFunc->zName==lagName ){
- int nArg = pWin->pOwner->x.pList->nExpr;
- int iEph = pMWin->iEphCsr;
- int csr = pWin->csrApp;
- int lbl = sqlite3VdbeMakeLabel(pParse);
- int tmpReg = sqlite3GetTempReg(pParse);
-
- if( nArg<3 ){
+ if( pMWin->regStartRowid ){
+ windowFullScan(p);
+ }else{
+ Parse *pParse = p->pParse;
+ Window *pWin;
+
+ for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
+ FuncDef *pFunc = pWin->pFunc;
+ if( pFunc->zName==nth_valueName
+ || pFunc->zName==first_valueName
+ ){
+ int csr = pWin->csrApp;
+ int lbl = sqlite3VdbeMakeLabel(pParse);
+ int tmpReg = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult);
- }else{
- sqlite3VdbeAddOp3(v, OP_Column, iEph, pWin->iArgCol+2, pWin->regResult);
- }
- sqlite3VdbeAddOp2(v, OP_Rowid, iEph, tmpReg);
- if( nArg<2 ){
- int val = (pFunc->zName==leadName ? 1 : -1);
- sqlite3VdbeAddOp2(v, OP_AddImm, tmpReg, val);
- }else{
- int op = (pFunc->zName==leadName ? OP_Add : OP_Subtract);
- int tmpReg2 = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp3(v, OP_Column, iEph, pWin->iArgCol+1, tmpReg2);
- sqlite3VdbeAddOp3(v, op, tmpReg2, tmpReg, tmpReg);
- sqlite3ReleaseTempReg(pParse, tmpReg2);
+
+ if( pFunc->zName==nth_valueName ){
+ sqlite3VdbeAddOp3(v, OP_Column,pMWin->iEphCsr,pWin->iArgCol+1,tmpReg);
+ windowCheckValue(pParse, tmpReg, 2);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, tmpReg);
+ }
+ sqlite3VdbeAddOp3(v, OP_Add, tmpReg, pWin->regApp, tmpReg);
+ sqlite3VdbeAddOp3(v, OP_Gt, pWin->regApp+1, lbl, tmpReg);
+ VdbeCoverageNeverNull(v);
+ sqlite3VdbeAddOp3(v, OP_SeekRowid, csr, 0, tmpReg);
+ VdbeCoverageNeverTaken(v);
+ sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol, pWin->regResult);
+ sqlite3VdbeResolveLabel(v, lbl);
+ sqlite3ReleaseTempReg(pParse, tmpReg);
+ }
+ else if( pFunc->zName==leadName || pFunc->zName==lagName ){
+ int nArg = pWin->pOwner->x.pList->nExpr;
+ int csr = pWin->csrApp;
+ int lbl = sqlite3VdbeMakeLabel(pParse);
+ int tmpReg = sqlite3GetTempReg(pParse);
+ int iEph = pMWin->iEphCsr;
+
+ if( nArg<3 ){
+ sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult);
+ }else{
+ sqlite3VdbeAddOp3(v, OP_Column, iEph,pWin->iArgCol+2,pWin->regResult);
+ }
+ sqlite3VdbeAddOp2(v, OP_Rowid, iEph, tmpReg);
+ if( nArg<2 ){
+ int val = (pFunc->zName==leadName ? 1 : -1);
+ sqlite3VdbeAddOp2(v, OP_AddImm, tmpReg, val);
+ }else{
+ int op = (pFunc->zName==leadName ? OP_Add : OP_Subtract);
+ int tmpReg2 = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp3(v, OP_Column, iEph, pWin->iArgCol+1, tmpReg2);
+ sqlite3VdbeAddOp3(v, op, tmpReg2, tmpReg, tmpReg);
+ sqlite3ReleaseTempReg(pParse, tmpReg2);
+ }
+
+ sqlite3VdbeAddOp3(v, OP_SeekRowid, csr, lbl, tmpReg);
+ VdbeCoverage(v);
+ sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol, pWin->regResult);
+ sqlite3VdbeResolveLabel(v, lbl);
+ sqlite3ReleaseTempReg(pParse, tmpReg);
}
-
- sqlite3VdbeAddOp3(v, OP_SeekRowid, csr, lbl, tmpReg);
- VdbeCoverage(v);
- sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol, pWin->regResult);
- sqlite3VdbeResolveLabel(v, lbl);
- sqlite3ReleaseTempReg(pParse, tmpReg);
}
}
- sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
-}
-
-/*
-** Invoke the code generated by windowReturnOneRow() and, optionally, the
-** xInverse() function for each window function, for one or more rows
-** from the Window.iEphCsr temp table. This routine generates VM code
-** similar to:
-**
-** while( regCtr>0 ){
-** regCtr--;
-** windowReturnOneRow()
-** if( bInverse ){
-** AggInverse
-** }
-** Next (Window.iEphCsr)
-** }
-*/
-static void windowReturnRows(
- Parse *pParse,
- Window *pMWin, /* List of window functions */
- int regCtr, /* Register containing number of rows */
- int regGosub, /* Register for Gosub addrGosub */
- int addrGosub, /* Address of sub-routine for ReturnOneRow */
- int regInvArg, /* Array of registers for xInverse args */
- int regInvSize /* Register containing size of partition */
-){
- int addr;
- Vdbe *v = sqlite3GetVdbe(pParse);
- windowAggFinal(pParse, pMWin, 0);
- addr = sqlite3VdbeAddOp3(v, OP_IfPos, regCtr, sqlite3VdbeCurrentAddr(v)+2 ,1);
- VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
- windowReturnOneRow(pParse, pMWin, regGosub, addrGosub);
- if( regInvArg ){
- windowAggStep(pParse, pMWin, pMWin->iEphCsr, 1, regInvArg, regInvSize);
- }
- sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, addr);
- VdbeCoverage(v);
- sqlite3VdbeJumpHere(v, addr+1); /* The OP_Goto */
+ sqlite3VdbeAddOp2(v, OP_Gosub, p->regGosub, p->addrGosub);
}
/*
@@ -146500,17 +147127,17 @@ static int windowInitAccum(Parse *pParse, Window *pMWin){
FuncDef *pFunc = pWin->pFunc;
sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum);
nArg = MAX(nArg, windowArgCount(pWin));
- if( pFunc->zName==nth_valueName
- || pFunc->zName==first_valueName
- ){
- sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp);
- sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp+1);
- }
+ if( pMWin->regStartRowid==0 ){
+ if( pFunc->zName==nth_valueName || pFunc->zName==first_valueName ){
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp+1);
+ }
- if( (pFunc->funcFlags & SQLITE_FUNC_MINMAX) && pWin->csrApp ){
- assert( pWin->eStart!=TK_UNBOUNDED );
- sqlite3VdbeAddOp1(v, OP_ResetSorter, pWin->csrApp);
- sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp+1);
+ if( (pFunc->funcFlags & SQLITE_FUNC_MINMAX) && pWin->csrApp ){
+ assert( pWin->eStart!=TK_UNBOUNDED );
+ sqlite3VdbeAddOp1(v, OP_ResetSorter, pWin->csrApp);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp+1);
+ }
}
}
regArg = pParse->nMem+1;
@@ -146518,672 +147145,248 @@ static int windowInitAccum(Parse *pParse, Window *pMWin){
return regArg;
}
+/*
+** Return true if the current frame should be cached in the ephemeral table,
+** even if there are no xInverse() calls required.
+*/
+static int windowCacheFrame(Window *pMWin){
+ Window *pWin;
+ if( pMWin->regStartRowid ) return 1;
+ for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
+ FuncDef *pFunc = pWin->pFunc;
+ if( (pFunc->zName==nth_valueName)
+ || (pFunc->zName==first_valueName)
+ || (pFunc->zName==leadName)
+ || (pFunc->zName==lagName)
+ ){
+ return 1;
+ }
+ }
+ return 0;
+}
/*
-** This function does the work of sqlite3WindowCodeStep() for all "ROWS"
-** window frame types except for "BETWEEN UNBOUNDED PRECEDING AND CURRENT
-** ROW". Pseudo-code for each follows.
-**
-** ROWS BETWEEN <expr1> PRECEDING AND <expr2> FOLLOWING
-**
-** ...
-** if( new partition ){
-** Gosub flush_partition
-** }
-** Insert (record in eph-table)
-** sqlite3WhereEnd()
-** Gosub flush_partition
-**
-** flush_partition:
-** Once {
-** OpenDup (iEphCsr -> csrStart)
-** OpenDup (iEphCsr -> csrEnd)
-** }
-** regStart = <expr1> // PRECEDING expression
-** regEnd = <expr2> // FOLLOWING expression
-** if( regStart<0 || regEnd<0 ){ error! }
-** Rewind (csr,csrStart,csrEnd) // if EOF goto flush_partition_done
-** Next(csrEnd) // if EOF skip Aggstep
-** Aggstep (csrEnd)
-** if( (regEnd--)<=0 ){
-** AggFinal (xValue)
-** Gosub addrGosub
-** Next(csr) // if EOF goto flush_partition_done
-** if( (regStart--)<=0 ){
-** AggInverse (csrStart)
-** Next(csrStart)
-** }
-** }
-** flush_partition_done:
-** ResetSorter (csr)
-** Return
-**
-** ROWS BETWEEN <expr> PRECEDING AND CURRENT ROW
-** ROWS BETWEEN CURRENT ROW AND <expr> FOLLOWING
-** ROWS BETWEEN UNBOUNDED PRECEDING AND <expr> FOLLOWING
-**
-** These are similar to the above. For "CURRENT ROW", intialize the
-** register to 0. For "UNBOUNDED PRECEDING" to infinity.
-**
-** ROWS BETWEEN <expr> PRECEDING AND UNBOUNDED FOLLOWING
-** ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-**
-** Rewind (csr,csrStart,csrEnd) // if EOF goto flush_partition_done
-** while( 1 ){
-** Next(csrEnd) // Exit while(1) at EOF
-** Aggstep (csrEnd)
-** }
-** while( 1 ){
-** AggFinal (xValue)
-** Gosub addrGosub
-** Next(csr) // if EOF goto flush_partition_done
-** if( (regStart--)<=0 ){
-** AggInverse (csrStart)
-** Next(csrStart)
-** }
-** }
-**
-** For the "CURRENT ROW AND UNBOUNDED FOLLOWING" case, the final if()
-** condition is always true (as if regStart were initialized to 0).
-**
-** RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-**
-** This is the only RANGE case handled by this routine. It modifies the
-** second while( 1 ) loop in "ROWS BETWEEN CURRENT ... UNBOUNDED..." to
-** be:
-**
-** while( 1 ){
-** AggFinal (xValue)
-** while( 1 ){
-** regPeer++
-** Gosub addrGosub
-** Next(csr) // if EOF goto flush_partition_done
-** if( new peer ) break;
-** }
-** while( (regPeer--)>0 ){
-** AggInverse (csrStart)
-** Next(csrStart)
-** }
-** }
-**
-** ROWS BETWEEN <expr> FOLLOWING AND <expr> FOLLOWING
-**
-** regEnd = regEnd - regStart
-** Rewind (csr,csrStart,csrEnd) // if EOF goto flush_partition_done
-** Aggstep (csrEnd)
-** Next(csrEnd) // if EOF fall-through
-** if( (regEnd--)<=0 ){
-** if( (regStart--)<=0 ){
-** AggFinal (xValue)
-** Gosub addrGosub
-** Next(csr) // if EOF goto flush_partition_done
-** }
-** AggInverse (csrStart)
-** Next (csrStart)
-** }
-**
-** ROWS BETWEEN <expr> PRECEDING AND <expr> PRECEDING
-**
-** Replace the bit after "Rewind" in the above with:
-**
-** if( (regEnd--)<=0 ){
-** AggStep (csrEnd)
-** Next (csrEnd)
-** }
-** AggFinal (xValue)
-** Gosub addrGosub
-** Next(csr) // if EOF goto flush_partition_done
-** if( (regStart--)<=0 ){
-** AggInverse (csr2)
-** Next (csr2)
-** }
+** regOld and regNew are each the first register in an array of size
+** pOrderBy->nExpr. This function generates code to compare the two
+** arrays of registers using the collation sequences and other comparison
+** parameters specified by pOrderBy.
**
+** If the two arrays are not equal, the contents of regNew is copied to
+** regOld and control falls through. Otherwise, if the contents of the arrays
+** are equal, an OP_Goto is executed. The address of the OP_Goto is returned.
*/
-static void windowCodeRowExprStep(
- Parse *pParse,
- Select *p,
- WhereInfo *pWInfo,
- int regGosub,
- int addrGosub
+static void windowIfNewPeer(
+ Parse *pParse,
+ ExprList *pOrderBy,
+ int regNew, /* First in array of new values */
+ int regOld, /* First in array of old values */
+ int addr /* Jump here */
){
- Window *pMWin = p->pWin;
Vdbe *v = sqlite3GetVdbe(pParse);
- int regFlushPart; /* Register for "Gosub flush_partition" */
- int lblFlushPart; /* Label for "Gosub flush_partition" */
- int lblFlushDone; /* Label for "Gosub flush_partition_done" */
-
- int regArg;
- int addr;
- int csrStart = pParse->nTab++;
- int csrEnd = pParse->nTab++;
- int regStart; /* Value of <expr> PRECEDING */
- int regEnd; /* Value of <expr> FOLLOWING */
- int addrGoto;
- int addrTop;
- int addrIfPos1 = 0;
- int addrIfPos2 = 0;
- int regSize = 0;
-
- assert( pMWin->eStart==TK_PRECEDING
- || pMWin->eStart==TK_CURRENT
- || pMWin->eStart==TK_FOLLOWING
- || pMWin->eStart==TK_UNBOUNDED
- );
- assert( pMWin->eEnd==TK_FOLLOWING
- || pMWin->eEnd==TK_CURRENT
- || pMWin->eEnd==TK_UNBOUNDED
- || pMWin->eEnd==TK_PRECEDING
- );
-
- /* Allocate register and label for the "flush_partition" sub-routine. */
- regFlushPart = ++pParse->nMem;
- lblFlushPart = sqlite3VdbeMakeLabel(pParse);
- lblFlushDone = sqlite3VdbeMakeLabel(pParse);
-
- regStart = ++pParse->nMem;
- regEnd = ++pParse->nMem;
-
- windowPartitionCache(pParse, p, pWInfo, regFlushPart, lblFlushPart, &regSize);
-
- addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
-
- /* Start of "flush_partition" */
- sqlite3VdbeResolveLabel(v, lblFlushPart);
- sqlite3VdbeAddOp2(v, OP_Once, 0, sqlite3VdbeCurrentAddr(v)+3);
- VdbeCoverage(v);
- VdbeComment((v, "Flush_partition subroutine"));
- sqlite3VdbeAddOp2(v, OP_OpenDup, csrStart, pMWin->iEphCsr);
- sqlite3VdbeAddOp2(v, OP_OpenDup, csrEnd, pMWin->iEphCsr);
-
- /* If either regStart or regEnd are not non-negative integers, throw
- ** an exception. */
- if( pMWin->pStart ){
- sqlite3ExprCode(pParse, pMWin->pStart, regStart);
- windowCheckIntValue(pParse, regStart, 0);
- }
- if( pMWin->pEnd ){
- sqlite3ExprCode(pParse, pMWin->pEnd, regEnd);
- windowCheckIntValue(pParse, regEnd, 1);
- }
-
- /* If this is "ROWS <expr1> FOLLOWING AND ROWS <expr2> FOLLOWING", do:
- **
- ** if( regEnd<regStart ){
- ** // The frame always consists of 0 rows
- ** regStart = regSize;
- ** }
- ** regEnd = regEnd - regStart;
- */
- if( pMWin->pEnd && pMWin->eStart==TK_FOLLOWING ){
- assert( pMWin->pStart!=0 );
- assert( pMWin->eEnd==TK_FOLLOWING );
- sqlite3VdbeAddOp3(v, OP_Ge, regStart, sqlite3VdbeCurrentAddr(v)+2, regEnd);
- VdbeCoverageNeverNull(v);
- sqlite3VdbeAddOp2(v, OP_Copy, regSize, regStart);
- sqlite3VdbeAddOp3(v, OP_Subtract, regStart, regEnd, regEnd);
- }
-
- if( pMWin->pStart && pMWin->eEnd==TK_PRECEDING ){
- assert( pMWin->pEnd!=0 );
- assert( pMWin->eStart==TK_PRECEDING );
- sqlite3VdbeAddOp3(v, OP_Le, regStart, sqlite3VdbeCurrentAddr(v)+3, regEnd);
- VdbeCoverageNeverNull(v);
- sqlite3VdbeAddOp2(v, OP_Copy, regSize, regStart);
- sqlite3VdbeAddOp2(v, OP_Copy, regSize, regEnd);
- }
-
- /* Initialize the accumulator register for each window function to NULL */
- regArg = windowInitAccum(pParse, pMWin);
-
- sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr, lblFlushDone);
- VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Rewind, csrStart, lblFlushDone);
- VdbeCoverageNeverTaken(v);
- sqlite3VdbeChangeP5(v, 1);
- sqlite3VdbeAddOp2(v, OP_Rewind, csrEnd, lblFlushDone);
- VdbeCoverageNeverTaken(v);
- sqlite3VdbeChangeP5(v, 1);
-
- /* Invoke AggStep function for each window function using the row that
- ** csrEnd currently points to. Or, if csrEnd is already at EOF,
- ** do nothing. */
- addrTop = sqlite3VdbeCurrentAddr(v);
- if( pMWin->eEnd==TK_PRECEDING ){
- addrIfPos1 = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0 , 1);
- VdbeCoverage(v);
- }
- sqlite3VdbeAddOp2(v, OP_Next, csrEnd, sqlite3VdbeCurrentAddr(v)+2);
- VdbeCoverage(v);
- addr = sqlite3VdbeAddOp0(v, OP_Goto);
- windowAggStep(pParse, pMWin, csrEnd, 0, regArg, regSize);
- if( pMWin->eEnd==TK_UNBOUNDED ){
- sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop);
- sqlite3VdbeJumpHere(v, addr);
- addrTop = sqlite3VdbeCurrentAddr(v);
+ if( pOrderBy ){
+ int nVal = pOrderBy->nExpr;
+ KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0);
+ sqlite3VdbeAddOp3(v, OP_Compare, regOld, regNew, nVal);
+ sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
+ sqlite3VdbeAddOp3(v, OP_Jump,
+ sqlite3VdbeCurrentAddr(v)+1, addr, sqlite3VdbeCurrentAddr(v)+1
+ );
+ VdbeCoverageEqNe(v);
+ sqlite3VdbeAddOp3(v, OP_Copy, regNew, regOld, nVal-1);
}else{
- sqlite3VdbeJumpHere(v, addr);
- if( pMWin->eEnd==TK_PRECEDING ){
- sqlite3VdbeJumpHere(v, addrIfPos1);
- }
- }
-
- if( pMWin->eEnd==TK_FOLLOWING ){
- addrIfPos1 = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0 , 1);
- VdbeCoverage(v);
- }
- if( pMWin->eStart==TK_FOLLOWING ){
- addrIfPos2 = sqlite3VdbeAddOp3(v, OP_IfPos, regStart, 0 , 1);
- VdbeCoverage(v);
- }
- windowAggFinal(pParse, pMWin, 0);
- windowReturnOneRow(pParse, pMWin, regGosub, addrGosub);
- sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)+2);
- VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, lblFlushDone);
- if( pMWin->eStart==TK_FOLLOWING ){
- sqlite3VdbeJumpHere(v, addrIfPos2);
- }
-
- if( pMWin->eStart==TK_CURRENT
- || pMWin->eStart==TK_PRECEDING
- || pMWin->eStart==TK_FOLLOWING
- ){
- int lblSkipInverse = sqlite3VdbeMakeLabel(pParse);;
- if( pMWin->eStart==TK_PRECEDING ){
- sqlite3VdbeAddOp3(v, OP_IfPos, regStart, lblSkipInverse, 1);
- VdbeCoverage(v);
- }
- if( pMWin->eStart==TK_FOLLOWING ){
- sqlite3VdbeAddOp2(v, OP_Next, csrStart, sqlite3VdbeCurrentAddr(v)+2);
- VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, lblSkipInverse);
- }else{
- sqlite3VdbeAddOp2(v, OP_Next, csrStart, sqlite3VdbeCurrentAddr(v)+1);
- VdbeCoverageAlwaysTaken(v);
- }
- windowAggStep(pParse, pMWin, csrStart, 1, regArg, regSize);
- sqlite3VdbeResolveLabel(v, lblSkipInverse);
- }
- if( pMWin->eEnd==TK_FOLLOWING ){
- sqlite3VdbeJumpHere(v, addrIfPos1);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addr);
}
- sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop);
-
- /* flush_partition_done: */
- sqlite3VdbeResolveLabel(v, lblFlushDone);
- sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr);
- sqlite3VdbeAddOp1(v, OP_Return, regFlushPart);
- VdbeComment((v, "end flush_partition subroutine"));
-
- /* Jump to here to skip over flush_partition */
- sqlite3VdbeJumpHere(v, addrGoto);
}
/*
-** This function does the work of sqlite3WindowCodeStep() for cases that
-** would normally be handled by windowCodeDefaultStep() when there are
-** one or more built-in window-functions that require the entire partition
-** to be cached in a temp table before any rows can be returned. Additionally.
-** "RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING" is always handled by
-** this function.
-**
-** Pseudo-code corresponding to the VM code generated by this function
-** for each type of window follows.
-**
-** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
-**
-** flush_partition:
-** Once {
-** OpenDup (iEphCsr -> csrLead)
-** }
-** Integer ctr 0
-** foreach row (csrLead){
-** if( new peer ){
-** AggFinal (xValue)
-** for(i=0; i<ctr; i++){
-** Gosub addrGosub
-** Next iEphCsr
-** }
-** Integer ctr 0
-** }
-** AggStep (csrLead)
-** Incr ctr
-** }
+** This function is called as part of generating VM programs for RANGE
+** offset PRECEDING/FOLLOWING frame boundaries. Assuming "ASC" order for
+** the ORDER BY term in the window, it generates code equivalent to:
**
-** AggFinal (xFinalize)
-** for(i=0; i<ctr; i++){
-** Gosub addrGosub
-** Next iEphCsr
-** }
-**
-** ResetSorter (csr)
-** Return
-**
-** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
-**
-** As above, except that the "if( new peer )" branch is always taken.
-**
-** RANGE BETWEEN CURRENT ROW AND CURRENT ROW
+** if( csr1.peerVal + regVal >= csr2.peerVal ) goto lbl;
**
-** As above, except that each of the for() loops becomes:
-**
-** for(i=0; i<ctr; i++){
-** Gosub addrGosub
-** AggInverse (iEphCsr)
-** Next iEphCsr
-** }
-**
-** RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
-**
-** flush_partition:
-** Once {
-** OpenDup (iEphCsr -> csrLead)
-** }
-** foreach row (csrLead) {
-** AggStep (csrLead)
-** }
-** foreach row (iEphCsr) {
-** Gosub addrGosub
-** }
-**
-** RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-**
-** flush_partition:
-** Once {
-** OpenDup (iEphCsr -> csrLead)
-** }
-** foreach row (csrLead){
-** AggStep (csrLead)
-** }
-** Rewind (csrLead)
-** Integer ctr 0
-** foreach row (csrLead){
-** if( new peer ){
-** AggFinal (xValue)
-** for(i=0; i<ctr; i++){
-** Gosub addrGosub
-** AggInverse (iEphCsr)
-** Next iEphCsr
-** }
-** Integer ctr 0
-** }
-** Incr ctr
-** }
-**
-** AggFinal (xFinalize)
-** for(i=0; i<ctr; i++){
-** Gosub addrGosub
-** Next iEphCsr
-** }
-**
-** ResetSorter (csr)
-** Return
+** A special type of arithmetic is used such that if csr.peerVal is not
+** a numeric type (real or integer), then the result of the addition is
+** a copy of csr1.peerVal.
*/
-static void windowCodeCacheStep(
- Parse *pParse,
- Select *p,
- WhereInfo *pWInfo,
- int regGosub,
- int addrGosub
+static void windowCodeRangeTest(
+ WindowCodeArg *p,
+ int op, /* OP_Ge or OP_Gt */
+ int csr1,
+ int regVal,
+ int csr2,
+ int lbl
){
- Window *pMWin = p->pWin;
+ Parse *pParse = p->pParse;
Vdbe *v = sqlite3GetVdbe(pParse);
- int k;
- int addr;
- ExprList *pPart = pMWin->pPartition;
- ExprList *pOrderBy = pMWin->pOrderBy;
- int nPeer = pOrderBy ? pOrderBy->nExpr : 0;
- int regNewPeer;
-
- int addrGoto; /* Address of Goto used to jump flush_par.. */
- int addrNext; /* Jump here for next iteration of loop */
- int regFlushPart;
- int lblFlushPart;
- int csrLead;
- int regCtr;
- int regArg; /* Register array to martial function args */
- int regSize;
- int lblEmpty;
- int bReverse = pMWin->pOrderBy && pMWin->eStart==TK_CURRENT
- && pMWin->eEnd==TK_UNBOUNDED;
-
- assert( (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_CURRENT)
- || (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_UNBOUNDED)
- || (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_CURRENT)
- || (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_UNBOUNDED)
- );
+ int reg1 = sqlite3GetTempReg(pParse);
+ int reg2 = sqlite3GetTempReg(pParse);
+ int arith = OP_Add;
+ int addrGe;
- lblEmpty = sqlite3VdbeMakeLabel(pParse);
- regNewPeer = pParse->nMem+1;
- pParse->nMem += nPeer;
+ int regString = ++pParse->nMem;
- /* Allocate register and label for the "flush_partition" sub-routine. */
- regFlushPart = ++pParse->nMem;
- lblFlushPart = sqlite3VdbeMakeLabel(pParse);
-
- csrLead = pParse->nTab++;
- regCtr = ++pParse->nMem;
-
- windowPartitionCache(pParse, p, pWInfo, regFlushPart, lblFlushPart, &regSize);
- addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
-
- /* Start of "flush_partition" */
- sqlite3VdbeResolveLabel(v, lblFlushPart);
- sqlite3VdbeAddOp2(v, OP_Once, 0, sqlite3VdbeCurrentAddr(v)+2);
- VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_OpenDup, csrLead, pMWin->iEphCsr);
-
- /* Initialize the accumulator register for each window function to NULL */
- regArg = windowInitAccum(pParse, pMWin);
-
- sqlite3VdbeAddOp2(v, OP_Integer, 0, regCtr);
- sqlite3VdbeAddOp2(v, OP_Rewind, csrLead, lblEmpty);
- VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr, lblEmpty);
- VdbeCoverageNeverTaken(v);
-
- if( bReverse ){
- int addr2 = sqlite3VdbeCurrentAddr(v);
- windowAggStep(pParse, pMWin, csrLead, 0, regArg, regSize);
- sqlite3VdbeAddOp2(v, OP_Next, csrLead, addr2);
- VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Rewind, csrLead, lblEmpty);
- VdbeCoverageNeverTaken(v);
- }
- addrNext = sqlite3VdbeCurrentAddr(v);
-
- if( pOrderBy && (pMWin->eEnd==TK_CURRENT || pMWin->eStart==TK_CURRENT) ){
- int bCurrent = (pMWin->eStart==TK_CURRENT);
- int addrJump = 0; /* Address of OP_Jump below */
- if( pMWin->eType==TK_RANGE ){
- int iOff = pMWin->nBufferCol + (pPart ? pPart->nExpr : 0);
- int regPeer = pMWin->regPart + (pPart ? pPart->nExpr : 0);
- KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0);
- for(k=0; k<nPeer; k++){
- sqlite3VdbeAddOp3(v, OP_Column, csrLead, iOff+k, regNewPeer+k);
- }
- addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPeer, regPeer, nPeer);
- sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
- addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2);
- VdbeCoverage(v);
- sqlite3VdbeAddOp3(v, OP_Copy, regNewPeer, regPeer, nPeer-1);
+ assert( op==OP_Ge || op==OP_Gt || op==OP_Le );
+ assert( p->pMWin->pOrderBy && p->pMWin->pOrderBy->nExpr==1 );
+ if( p->pMWin->pOrderBy->a[0].sortOrder ){
+ switch( op ){
+ case OP_Ge: op = OP_Le; break;
+ case OP_Gt: op = OP_Lt; break;
+ default: assert( op==OP_Le ); op = OP_Ge; break;
}
-
- windowReturnRows(pParse, pMWin, regCtr, regGosub, addrGosub,
- (bCurrent ? regArg : 0), (bCurrent ? regSize : 0)
- );
- if( addrJump ) sqlite3VdbeJumpHere(v, addrJump);
+ arith = OP_Subtract;
}
- if( bReverse==0 ){
- windowAggStep(pParse, pMWin, csrLead, 0, regArg, regSize);
- }
- sqlite3VdbeAddOp2(v, OP_AddImm, regCtr, 1);
- sqlite3VdbeAddOp2(v, OP_Next, csrLead, addrNext);
- VdbeCoverage(v);
-
- windowReturnRows(pParse, pMWin, regCtr, regGosub, addrGosub, 0, 0);
+ windowReadPeerValues(p, csr1, reg1);
+ windowReadPeerValues(p, csr2, reg2);
- sqlite3VdbeResolveLabel(v, lblEmpty);
- sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr);
- sqlite3VdbeAddOp1(v, OP_Return, regFlushPart);
+ /* Check if the peer value for csr1 value is a text or blob by comparing
+ ** it to the smallest possible string - ''. If it is, jump over the
+ ** OP_Add or OP_Subtract operation and proceed directly to the comparison. */
+ sqlite3VdbeAddOp4(v, OP_String8, 0, regString, 0, "", P4_STATIC);
+ addrGe = sqlite3VdbeAddOp3(v, OP_Ge, regString, 0, reg1);
+ VdbeCoverage(v);
+ sqlite3VdbeAddOp3(v, arith, regVal, reg1, reg1);
+ sqlite3VdbeJumpHere(v, addrGe);
+ sqlite3VdbeAddOp3(v, op, reg2, lbl, reg1); VdbeCoverage(v);
+ sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
+ assert( op==OP_Ge || op==OP_Gt || op==OP_Lt || op==OP_Le );
+ testcase(op==OP_Ge); VdbeCoverageIf(v, op==OP_Ge);
+ testcase(op==OP_Lt); VdbeCoverageIf(v, op==OP_Lt);
+ testcase(op==OP_Le); VdbeCoverageIf(v, op==OP_Le);
+ testcase(op==OP_Gt); VdbeCoverageIf(v, op==OP_Gt);
- /* Jump to here to skip over flush_partition */
- sqlite3VdbeJumpHere(v, addrGoto);
+ sqlite3ReleaseTempReg(pParse, reg1);
+ sqlite3ReleaseTempReg(pParse, reg2);
}
-
/*
-** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
-**
-** ...
-** if( new partition ){
-** AggFinal (xFinalize)
-** Gosub addrGosub
-** ResetSorter eph-table
-** }
-** else if( new peer ){
-** AggFinal (xValue)
-** Gosub addrGosub
-** ResetSorter eph-table
-** }
-** AggStep
-** Insert (record into eph-table)
-** sqlite3WhereEnd()
-** AggFinal (xFinalize)
-** Gosub addrGosub
-**
-** RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
-**
-** As above, except take no action for a "new peer". Invoke
-** the sub-routine once only for each partition.
-**
-** RANGE BETWEEN CURRENT ROW AND CURRENT ROW
-**
-** As above, except that the "new peer" condition is handled in the
-** same way as "new partition" (so there is no "else if" block).
-**
-** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
-**
-** As above, except assume every row is a "new peer".
+** Helper function for sqlite3WindowCodeStep(). Each call to this function
+** generates VM code for a single RETURN_ROW, AGGSTEP or AGGINVERSE
+** operation. Refer to the header comment for sqlite3WindowCodeStep() for
+** details.
*/
-static void windowCodeDefaultStep(
- Parse *pParse,
- Select *p,
- WhereInfo *pWInfo,
- int regGosub,
- int addrGosub
+static int windowCodeOp(
+ WindowCodeArg *p, /* Context object */
+ int op, /* WINDOW_RETURN_ROW, AGGSTEP or AGGINVERSE */
+ int regCountdown, /* Register for OP_IfPos countdown */
+ int jumpOnEof /* Jump here if stepped cursor reaches EOF */
){
- Window *pMWin = p->pWin;
- Vdbe *v = sqlite3GetVdbe(pParse);
- int k;
- int iSubCsr = p->pSrc->a[0].iCursor;
- int nSub = p->pSrc->a[0].pTab->nCol;
- int reg = pParse->nMem+1;
- int regRecord = reg+nSub;
- int regRowid = regRecord+1;
- int addr;
- ExprList *pPart = pMWin->pPartition;
- ExprList *pOrderBy = pMWin->pOrderBy;
-
- assert( pMWin->eType==TK_RANGE
- || (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_CURRENT)
- );
-
- assert( (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_CURRENT)
- || (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_UNBOUNDED)
- || (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_CURRENT)
- || (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_UNBOUNDED && !pOrderBy)
- );
-
- if( pMWin->eEnd==TK_UNBOUNDED ){
- pOrderBy = 0;
- }
-
- pParse->nMem += nSub + 2;
-
- /* Load the individual column values of the row returned by
- ** the sub-select into an array of registers. */
- for(k=0; k<nSub; k++){
- sqlite3VdbeAddOp3(v, OP_Column, iSubCsr, k, reg+k);
+ int csr, reg;
+ Parse *pParse = p->pParse;
+ Window *pMWin = p->pMWin;
+ int ret = 0;
+ Vdbe *v = p->pVdbe;
+ int addrIf = 0;
+ int addrContinue = 0;
+ int addrGoto = 0;
+ int bPeer = (pMWin->eFrmType!=TK_ROWS);
+
+ int lblDone = sqlite3VdbeMakeLabel(pParse);
+ int addrNextRange = 0;
+
+ /* Special case - WINDOW_AGGINVERSE is always a no-op if the frame
+ ** starts with UNBOUNDED PRECEDING. */
+ if( op==WINDOW_AGGINVERSE && pMWin->eStart==TK_UNBOUNDED ){
+ assert( regCountdown==0 && jumpOnEof==0 );
+ return 0;
}
- /* Check if this is the start of a new partition or peer group. */
- if( pPart || pOrderBy ){
- int nPart = (pPart ? pPart->nExpr : 0);
- int addrGoto = 0;
- int addrJump = 0;
- int nPeer = (pOrderBy ? pOrderBy->nExpr : 0);
-
- if( pPart ){
- int regNewPart = reg + pMWin->nBufferCol;
- KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pPart, 0, 0);
- addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, pMWin->regPart,nPart);
- sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
- addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2);
- VdbeCoverageEqNe(v);
- windowAggFinal(pParse, pMWin, 1);
- if( pOrderBy ){
- addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
+ if( regCountdown>0 ){
+ if( pMWin->eFrmType==TK_RANGE ){
+ addrNextRange = sqlite3VdbeCurrentAddr(v);
+ assert( op==WINDOW_AGGINVERSE || op==WINDOW_AGGSTEP );
+ if( op==WINDOW_AGGINVERSE ){
+ if( pMWin->eStart==TK_FOLLOWING ){
+ windowCodeRangeTest(
+ p, OP_Le, p->current.csr, regCountdown, p->start.csr, lblDone
+ );
+ }else{
+ windowCodeRangeTest(
+ p, OP_Ge, p->start.csr, regCountdown, p->current.csr, lblDone
+ );
+ }
+ }else{
+ windowCodeRangeTest(
+ p, OP_Gt, p->end.csr, regCountdown, p->current.csr, lblDone
+ );
}
+ }else{
+ addrIf = sqlite3VdbeAddOp3(v, OP_IfPos, regCountdown, 0, 1);
+ VdbeCoverage(v);
}
+ }
- if( pOrderBy ){
- int regNewPeer = reg + pMWin->nBufferCol + nPart;
- int regPeer = pMWin->regPart + nPart;
+ if( op==WINDOW_RETURN_ROW && pMWin->regStartRowid==0 ){
+ windowAggFinal(p, 0);
+ }
+ addrContinue = sqlite3VdbeCurrentAddr(v);
+ switch( op ){
+ case WINDOW_RETURN_ROW:
+ csr = p->current.csr;
+ reg = p->current.reg;
+ windowReturnOneRow(p);
+ break;
- if( addrJump ) sqlite3VdbeJumpHere(v, addrJump);
- if( pMWin->eType==TK_RANGE ){
- KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0);
- addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPeer, regPeer, nPeer);
- sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
- addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2);
- VdbeCoverage(v);
+ case WINDOW_AGGINVERSE:
+ csr = p->start.csr;
+ reg = p->start.reg;
+ if( pMWin->regStartRowid ){
+ assert( pMWin->regEndRowid );
+ sqlite3VdbeAddOp2(v, OP_AddImm, pMWin->regStartRowid, 1);
}else{
- addrJump = 0;
+ windowAggStep(pParse, pMWin, csr, 1, p->regArg);
}
- windowAggFinal(pParse, pMWin, pMWin->eStart==TK_CURRENT);
- if( addrGoto ) sqlite3VdbeJumpHere(v, addrGoto);
- }
-
- sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr,sqlite3VdbeCurrentAddr(v)+3);
- VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
- sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)-1);
- VdbeCoverage(v);
-
- sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr);
- sqlite3VdbeAddOp3(
- v, OP_Copy, reg+pMWin->nBufferCol, pMWin->regPart, nPart+nPeer-1
- );
+ break;
- if( addrJump ) sqlite3VdbeJumpHere(v, addrJump);
+ default:
+ assert( op==WINDOW_AGGSTEP );
+ csr = p->end.csr;
+ reg = p->end.reg;
+ if( pMWin->regStartRowid ){
+ assert( pMWin->regEndRowid );
+ sqlite3VdbeAddOp2(v, OP_AddImm, pMWin->regEndRowid, 1);
+ }else{
+ windowAggStep(pParse, pMWin, csr, 0, p->regArg);
+ }
+ break;
}
- /* Invoke step function for window functions */
- windowAggStep(pParse, pMWin, -1, 0, reg, 0);
+ if( op==p->eDelete ){
+ sqlite3VdbeAddOp1(v, OP_Delete, csr);
+ sqlite3VdbeChangeP5(v, OPFLAG_SAVEPOSITION);
+ }
- /* Buffer the current row in the ephemeral table. */
- if( pMWin->nBufferCol>0 ){
- sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, pMWin->nBufferCol, regRecord);
+ if( jumpOnEof ){
+ sqlite3VdbeAddOp2(v, OP_Next, csr, sqlite3VdbeCurrentAddr(v)+2);
+ VdbeCoverage(v);
+ ret = sqlite3VdbeAddOp0(v, OP_Goto);
}else{
- sqlite3VdbeAddOp2(v, OP_Blob, 0, regRecord);
- sqlite3VdbeAppendP4(v, (void*)"", 0);
+ sqlite3VdbeAddOp2(v, OP_Next, csr, sqlite3VdbeCurrentAddr(v)+1+bPeer);
+ VdbeCoverage(v);
+ if( bPeer ){
+ addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
+ }
}
- sqlite3VdbeAddOp2(v, OP_NewRowid, pMWin->iEphCsr, regRowid);
- sqlite3VdbeAddOp3(v, OP_Insert, pMWin->iEphCsr, regRecord, regRowid);
- /* End the database scan loop. */
- sqlite3WhereEnd(pWInfo);
+ if( bPeer ){
+ int nReg = (pMWin->pOrderBy ? pMWin->pOrderBy->nExpr : 0);
+ int regTmp = (nReg ? sqlite3GetTempRange(pParse, nReg) : 0);
+ windowReadPeerValues(p, csr, regTmp);
+ windowIfNewPeer(pParse, pMWin->pOrderBy, regTmp, reg, addrContinue);
+ sqlite3ReleaseTempRange(pParse, regTmp, nReg);
+ }
- windowAggFinal(pParse, pMWin, 1);
- sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr,sqlite3VdbeCurrentAddr(v)+3);
- VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
- sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)-1);
- VdbeCoverage(v);
+ if( addrNextRange ){
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNextRange);
+ }
+ sqlite3VdbeResolveLabel(v, lblDone);
+ if( addrGoto ) sqlite3VdbeJumpHere(v, addrGoto);
+ if( addrIf ) sqlite3VdbeJumpHere(v, addrIf);
+ return ret;
}
+
/*
** Allocate and return a duplicate of the Window object indicated by the
** third argument. Set the Window.pOwner field of the new object to
@@ -147199,9 +147402,10 @@ SQLITE_PRIVATE Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p){
pNew->pFunc = p->pFunc;
pNew->pPartition = sqlite3ExprListDup(db, p->pPartition, 0);
pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy, 0);
- pNew->eType = p->eType;
+ pNew->eFrmType = p->eFrmType;
pNew->eEnd = p->eEnd;
pNew->eStart = p->eStart;
+ pNew->eExclude = p->eExclude;
pNew->pStart = sqlite3ExprDup(db, p->pStart, 0);
pNew->pEnd = sqlite3ExprDup(db, p->pEnd, 0);
pNew->pOwner = pOwner;
@@ -147229,11 +147433,359 @@ SQLITE_PRIVATE Window *sqlite3WindowListDup(sqlite3 *db, Window *p){
}
/*
+** Return true if it can be determined at compile time that expression
+** pExpr evaluates to a value that, when cast to an integer, is greater
+** than zero. False otherwise.
+**
+** If an OOM error occurs, this function sets the Parse.db.mallocFailed
+** flag and returns zero.
+*/
+static int windowExprGtZero(Parse *pParse, Expr *pExpr){
+ int ret = 0;
+ sqlite3 *db = pParse->db;
+ sqlite3_value *pVal = 0;
+ sqlite3ValueFromExpr(db, pExpr, db->enc, SQLITE_AFF_NUMERIC, &pVal);
+ if( pVal && sqlite3_value_int(pVal)>0 ){
+ ret = 1;
+ }
+ sqlite3ValueFree(pVal);
+ return ret;
+}
+
+/*
** sqlite3WhereBegin() has already been called for the SELECT statement
** passed as the second argument when this function is invoked. It generates
-** code to populate the Window.regResult register for each window function and
-** invoke the sub-routine at instruction addrGosub once for each row.
-** This function calls sqlite3WhereEnd() before returning.
+** code to populate the Window.regResult register for each window function
+** and invoke the sub-routine at instruction addrGosub once for each row.
+** sqlite3WhereEnd() is always called before returning.
+**
+** This function handles several different types of window frames, which
+** require slightly different processing. The following pseudo code is
+** used to implement window frames of the form:
+**
+** ROWS BETWEEN <expr1> PRECEDING AND <expr2> FOLLOWING
+**
+** Other window frame types use variants of the following:
+**
+** ... loop started by sqlite3WhereBegin() ...
+** if( new partition ){
+** Gosub flush
+** }
+** Insert new row into eph table.
+**
+** if( first row of partition ){
+** // Rewind three cursors, all open on the eph table.
+** Rewind(csrEnd);
+** Rewind(csrStart);
+** Rewind(csrCurrent);
+**
+** regEnd = <expr2> // FOLLOWING expression
+** regStart = <expr1> // PRECEDING expression
+** }else{
+** // First time this branch is taken, the eph table contains two
+** // rows. The first row in the partition, which all three cursors
+** // currently point to, and the following row.
+** AGGSTEP
+** if( (regEnd--)<=0 ){
+** RETURN_ROW
+** if( (regStart--)<=0 ){
+** AGGINVERSE
+** }
+** }
+** }
+** }
+** flush:
+** AGGSTEP
+** while( 1 ){
+** RETURN ROW
+** if( csrCurrent is EOF ) break;
+** if( (regStart--)<=0 ){
+** AggInverse(csrStart)
+** Next(csrStart)
+** }
+** }
+**
+** The pseudo-code above uses the following shorthand:
+**
+** AGGSTEP: invoke the aggregate xStep() function for each window function
+** with arguments read from the current row of cursor csrEnd, then
+** step cursor csrEnd forward one row (i.e. sqlite3BtreeNext()).
+**
+** RETURN_ROW: return a row to the caller based on the contents of the
+** current row of csrCurrent and the current state of all
+** aggregates. Then step cursor csrCurrent forward one row.
+**
+** AGGINVERSE: invoke the aggregate xInverse() function for each window
+** functions with arguments read from the current row of cursor
+** csrStart. Then step csrStart forward one row.
+**
+** There are two other ROWS window frames that are handled significantly
+** differently from the above - "BETWEEN <expr> PRECEDING AND <expr> PRECEDING"
+** and "BETWEEN <expr> FOLLOWING AND <expr> FOLLOWING". These are special
+** cases because they change the order in which the three cursors (csrStart,
+** csrCurrent and csrEnd) iterate through the ephemeral table. Cases that
+** use UNBOUNDED or CURRENT ROW are much simpler variations on one of these
+** three.
+**
+** ROWS BETWEEN <expr1> PRECEDING AND <expr2> PRECEDING
+**
+** ... loop started by sqlite3WhereBegin() ...
+** if( new partition ){
+** Gosub flush
+** }
+** Insert new row into eph table.
+** if( first row of partition ){
+** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent)
+** regEnd = <expr2>
+** regStart = <expr1>
+** }else{
+** if( (regEnd--)<=0 ){
+** AGGSTEP
+** }
+** RETURN_ROW
+** if( (regStart--)<=0 ){
+** AGGINVERSE
+** }
+** }
+** }
+** flush:
+** if( (regEnd--)<=0 ){
+** AGGSTEP
+** }
+** RETURN_ROW
+**
+**
+** ROWS BETWEEN <expr1> FOLLOWING AND <expr2> FOLLOWING
+**
+** ... loop started by sqlite3WhereBegin() ...
+** if( new partition ){
+** Gosub flush
+** }
+** Insert new row into eph table.
+** if( first row of partition ){
+** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent)
+** regEnd = <expr2>
+** regStart = regEnd - <expr1>
+** }else{
+** AGGSTEP
+** if( (regEnd--)<=0 ){
+** RETURN_ROW
+** }
+** if( (regStart--)<=0 ){
+** AGGINVERSE
+** }
+** }
+** }
+** flush:
+** AGGSTEP
+** while( 1 ){
+** if( (regEnd--)<=0 ){
+** RETURN_ROW
+** if( eof ) break;
+** }
+** if( (regStart--)<=0 ){
+** AGGINVERSE
+** if( eof ) break
+** }
+** }
+** while( !eof csrCurrent ){
+** RETURN_ROW
+** }
+**
+** For the most part, the patterns above are adapted to support UNBOUNDED by
+** assuming that it is equivalent to "infinity PRECEDING/FOLLOWING" and
+** CURRENT ROW by assuming that it is equivilent to "0 PRECEDING/FOLLOWING".
+** This is optimized of course - branches that will never be taken and
+** conditions that are always true are omitted from the VM code. The only
+** exceptional case is:
+**
+** ROWS BETWEEN <expr1> FOLLOWING AND UNBOUNDED FOLLOWING
+**
+** ... loop started by sqlite3WhereBegin() ...
+** if( new partition ){
+** Gosub flush
+** }
+** Insert new row into eph table.
+** if( first row of partition ){
+** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent)
+** regStart = <expr1>
+** }else{
+** AGGSTEP
+** }
+** }
+** flush:
+** AGGSTEP
+** while( 1 ){
+** if( (regStart--)<=0 ){
+** AGGINVERSE
+** if( eof ) break
+** }
+** RETURN_ROW
+** }
+** while( !eof csrCurrent ){
+** RETURN_ROW
+** }
+**
+** Also requiring special handling are the cases:
+**
+** ROWS BETWEEN <expr1> PRECEDING AND <expr2> PRECEDING
+** ROWS BETWEEN <expr1> FOLLOWING AND <expr2> FOLLOWING
+**
+** when (expr1 < expr2). This is detected at runtime, not by this function.
+** To handle this case, the pseudo-code programs depicted above are modified
+** slightly to be:
+**
+** ... loop started by sqlite3WhereBegin() ...
+** if( new partition ){
+** Gosub flush
+** }
+** Insert new row into eph table.
+** if( first row of partition ){
+** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent)
+** regEnd = <expr2>
+** regStart = <expr1>
+** if( regEnd < regStart ){
+** RETURN_ROW
+** delete eph table contents
+** continue
+** }
+** ...
+**
+** The new "continue" statement in the above jumps to the next iteration
+** of the outer loop - the one started by sqlite3WhereBegin().
+**
+** The various GROUPS cases are implemented using the same patterns as
+** ROWS. The VM code is modified slightly so that:
+**
+** 1. The else branch in the main loop is only taken if the row just
+** added to the ephemeral table is the start of a new group. In
+** other words, it becomes:
+**
+** ... loop started by sqlite3WhereBegin() ...
+** if( new partition ){
+** Gosub flush
+** }
+** Insert new row into eph table.
+** if( first row of partition ){
+** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent)
+** regEnd = <expr2>
+** regStart = <expr1>
+** }else if( new group ){
+** ...
+** }
+** }
+**
+** 2. Instead of processing a single row, each RETURN_ROW, AGGSTEP or
+** AGGINVERSE step processes the current row of the relevant cursor and
+** all subsequent rows belonging to the same group.
+**
+** RANGE window frames are a little different again. As for GROUPS, the
+** main loop runs once per group only. And RETURN_ROW, AGGSTEP and AGGINVERSE
+** deal in groups instead of rows. As for ROWS and GROUPS, there are three
+** basic cases:
+**
+** RANGE BETWEEN <expr1> PRECEDING AND <expr2> FOLLOWING
+**
+** ... loop started by sqlite3WhereBegin() ...
+** if( new partition ){
+** Gosub flush
+** }
+** Insert new row into eph table.
+** if( first row of partition ){
+** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent)
+** regEnd = <expr2>
+** regStart = <expr1>
+** }else{
+** AGGSTEP
+** while( (csrCurrent.key + regEnd) < csrEnd.key ){
+** RETURN_ROW
+** while( csrStart.key + regStart) < csrCurrent.key ){
+** AGGINVERSE
+** }
+** }
+** }
+** }
+** flush:
+** AGGSTEP
+** while( 1 ){
+** RETURN ROW
+** if( csrCurrent is EOF ) break;
+** while( csrStart.key + regStart) < csrCurrent.key ){
+** AGGINVERSE
+** }
+** }
+** }
+**
+** In the above notation, "csr.key" means the current value of the ORDER BY
+** expression (there is only ever 1 for a RANGE that uses an <expr> FOLLOWING
+** or <expr PRECEDING) read from cursor csr.
+**
+** RANGE BETWEEN <expr1> PRECEDING AND <expr2> PRECEDING
+**
+** ... loop started by sqlite3WhereBegin() ...
+** if( new partition ){
+** Gosub flush
+** }
+** Insert new row into eph table.
+** if( first row of partition ){
+** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent)
+** regEnd = <expr2>
+** regStart = <expr1>
+** }else{
+** if( (csrEnd.key + regEnd) <= csrCurrent.key ){
+** AGGSTEP
+** }
+** while( (csrStart.key + regStart) < csrCurrent.key ){
+** AGGINVERSE
+** }
+** RETURN_ROW
+** }
+** }
+** flush:
+** while( (csrEnd.key + regEnd) <= csrCurrent.key ){
+** AGGSTEP
+** }
+** while( (csrStart.key + regStart) < csrCurrent.key ){
+** AGGINVERSE
+** }
+** RETURN_ROW
+**
+** RANGE BETWEEN <expr1> FOLLOWING AND <expr2> FOLLOWING
+**
+** ... loop started by sqlite3WhereBegin() ...
+** if( new partition ){
+** Gosub flush
+** }
+** Insert new row into eph table.
+** if( first row of partition ){
+** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent)
+** regEnd = <expr2>
+** regStart = <expr1>
+** }else{
+** AGGSTEP
+** while( (csrCurrent.key + regEnd) < csrEnd.key ){
+** while( (csrCurrent.key + regStart) > csrStart.key ){
+** AGGINVERSE
+** }
+** RETURN_ROW
+** }
+** }
+** }
+** flush:
+** AGGSTEP
+** while( 1 ){
+** while( (csrCurrent.key + regStart) > csrStart.key ){
+** AGGINVERSE
+** if( eof ) break "while( 1 )" loop.
+** }
+** RETURN_ROW
+** }
+** while( !eof csrCurrent ){
+** RETURN_ROW
+** }
+**
+** The text above leaves out many details. Refer to the code and comments
+** below for a more complete picture.
*/
SQLITE_PRIVATE void sqlite3WindowCodeStep(
Parse *pParse, /* Parse context */
@@ -147243,75 +147795,321 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep(
int addrGosub /* OP_Gosub here to return each row */
){
Window *pMWin = p->pWin;
+ ExprList *pOrderBy = pMWin->pOrderBy;
+ Vdbe *v = sqlite3GetVdbe(pParse);
+ int csrWrite; /* Cursor used to write to eph. table */
+ int csrInput = p->pSrc->a[0].iCursor; /* Cursor of sub-select */
+ int nInput = p->pSrc->a[0].pTab->nCol; /* Number of cols returned by sub */
+ int iInput; /* To iterate through sub cols */
+ int addrNe; /* Address of OP_Ne */
+ int addrGosubFlush = 0; /* Address of OP_Gosub to flush: */
+ int addrInteger = 0; /* Address of OP_Integer */
+ int addrEmpty; /* Address of OP_Rewind in flush: */
+ int regStart = 0; /* Value of <expr> PRECEDING */
+ int regEnd = 0; /* Value of <expr> FOLLOWING */
+ int regNew; /* Array of registers holding new input row */
+ int regRecord; /* regNew array in record form */
+ int regRowid; /* Rowid for regRecord in eph table */
+ int regNewPeer = 0; /* Peer values for new row (part of regNew) */
+ int regPeer = 0; /* Peer values for current row */
+ int regFlushPart = 0; /* Register for "Gosub flush_partition" */
+ WindowCodeArg s; /* Context object for sub-routines */
+ int lblWhereEnd; /* Label just before sqlite3WhereEnd() code */
+
+ assert( pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_CURRENT
+ || pMWin->eStart==TK_FOLLOWING || pMWin->eStart==TK_UNBOUNDED
+ );
+ assert( pMWin->eEnd==TK_FOLLOWING || pMWin->eEnd==TK_CURRENT
+ || pMWin->eEnd==TK_UNBOUNDED || pMWin->eEnd==TK_PRECEDING
+ );
+ assert( pMWin->eExclude==0 || pMWin->eExclude==TK_CURRENT
+ || pMWin->eExclude==TK_GROUP || pMWin->eExclude==TK_TIES
+ || pMWin->eExclude==TK_NO
+ );
- /* There are three different functions that may be used to do the work
- ** of this one, depending on the window frame and the specific built-in
- ** window functions used (if any).
- **
- ** windowCodeRowExprStep() handles all "ROWS" window frames, except for:
- **
- ** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
- **
- ** The exception is because windowCodeRowExprStep() implements all window
- ** frame types by caching the entire partition in a temp table, and
- ** "ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW" is easy enough to
- ** implement without such a cache.
- **
- ** windowCodeCacheStep() is used for:
- **
- ** RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- **
- ** It is also used for anything not handled by windowCodeRowExprStep()
- ** that invokes a built-in window function that requires the entire
- ** partition to be cached in a temp table before any rows are returned
- ** (e.g. nth_value() or percent_rank()).
- **
- ** Finally, assuming there is no built-in window function that requires
- ** the partition to be cached, windowCodeDefaultStep() is used for:
- **
- ** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
- ** RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
- ** RANGE BETWEEN CURRENT ROW AND CURRENT ROW
- ** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
- **
- ** windowCodeDefaultStep() is the only one of the three functions that
- ** does not cache each partition in a temp table before beginning to
- ** return rows.
- */
- if( pMWin->eType==TK_ROWS
- && (pMWin->eStart!=TK_UNBOUNDED||pMWin->eEnd!=TK_CURRENT||!pMWin->pOrderBy)
- ){
- VdbeModuleComment((pParse->pVdbe, "Begin RowExprStep()"));
- windowCodeRowExprStep(pParse, p, pWInfo, regGosub, addrGosub);
- }else{
- Window *pWin;
- int bCache = 0; /* True to use CacheStep() */
-
- if( pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_UNBOUNDED ){
- bCache = 1;
- }else{
- for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
- FuncDef *pFunc = pWin->pFunc;
- if( (pFunc->funcFlags & SQLITE_FUNC_WINDOW_SIZE)
- || (pFunc->zName==nth_valueName)
- || (pFunc->zName==first_valueName)
- || (pFunc->zName==leadName)
- || (pFunc->zName==lagName)
- ){
- bCache = 1;
- break;
+ lblWhereEnd = sqlite3VdbeMakeLabel(pParse);
+
+ /* Fill in the context object */
+ memset(&s, 0, sizeof(WindowCodeArg));
+ s.pParse = pParse;
+ s.pMWin = pMWin;
+ s.pVdbe = v;
+ s.regGosub = regGosub;
+ s.addrGosub = addrGosub;
+ s.current.csr = pMWin->iEphCsr;
+ csrWrite = s.current.csr+1;
+ s.start.csr = s.current.csr+2;
+ s.end.csr = s.current.csr+3;
+
+ /* Figure out when rows may be deleted from the ephemeral table. There
+ ** are four options - they may never be deleted (eDelete==0), they may
+ ** be deleted as soon as they are no longer part of the window frame
+ ** (eDelete==WINDOW_AGGINVERSE), they may be deleted as after the row
+ ** has been returned to the caller (WINDOW_RETURN_ROW), or they may
+ ** be deleted after they enter the frame (WINDOW_AGGSTEP). */
+ switch( pMWin->eStart ){
+ case TK_FOLLOWING:
+ if( pMWin->eFrmType!=TK_RANGE
+ && windowExprGtZero(pParse, pMWin->pStart)
+ ){
+ s.eDelete = WINDOW_RETURN_ROW;
+ }
+ break;
+ case TK_UNBOUNDED:
+ if( windowCacheFrame(pMWin)==0 ){
+ if( pMWin->eEnd==TK_PRECEDING ){
+ if( pMWin->eFrmType!=TK_RANGE
+ && windowExprGtZero(pParse, pMWin->pEnd)
+ ){
+ s.eDelete = WINDOW_AGGSTEP;
+ }
+ }else{
+ s.eDelete = WINDOW_RETURN_ROW;
}
}
+ break;
+ default:
+ s.eDelete = WINDOW_AGGINVERSE;
+ break;
+ }
+
+ /* Allocate registers for the array of values from the sub-query, the
+ ** samve values in record form, and the rowid used to insert said record
+ ** into the ephemeral table. */
+ regNew = pParse->nMem+1;
+ pParse->nMem += nInput;
+ regRecord = ++pParse->nMem;
+ regRowid = ++pParse->nMem;
+
+ /* If the window frame contains an "<expr> PRECEDING" or "<expr> FOLLOWING"
+ ** clause, allocate registers to store the results of evaluating each
+ ** <expr>. */
+ if( pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_FOLLOWING ){
+ regStart = ++pParse->nMem;
+ }
+ if( pMWin->eEnd==TK_PRECEDING || pMWin->eEnd==TK_FOLLOWING ){
+ regEnd = ++pParse->nMem;
+ }
+
+ /* If this is not a "ROWS BETWEEN ..." frame, then allocate arrays of
+ ** registers to store copies of the ORDER BY expressions (peer values)
+ ** for the main loop, and for each cursor (start, current and end). */
+ if( pMWin->eFrmType!=TK_ROWS ){
+ int nPeer = (pOrderBy ? pOrderBy->nExpr : 0);
+ regNewPeer = regNew + pMWin->nBufferCol;
+ if( pMWin->pPartition ) regNewPeer += pMWin->pPartition->nExpr;
+ regPeer = pParse->nMem+1; pParse->nMem += nPeer;
+ s.start.reg = pParse->nMem+1; pParse->nMem += nPeer;
+ s.current.reg = pParse->nMem+1; pParse->nMem += nPeer;
+ s.end.reg = pParse->nMem+1; pParse->nMem += nPeer;
+ }
+
+ /* Load the column values for the row returned by the sub-select
+ ** into an array of registers starting at regNew. Assemble them into
+ ** a record in register regRecord. */
+ for(iInput=0; iInput<nInput; iInput++){
+ sqlite3VdbeAddOp3(v, OP_Column, csrInput, iInput, regNew+iInput);
+ }
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regNew, nInput, regRecord);
+
+ /* An input row has just been read into an array of registers starting
+ ** at regNew. If the window has a PARTITION clause, this block generates
+ ** VM code to check if the input row is the start of a new partition.
+ ** If so, it does an OP_Gosub to an address to be filled in later. The
+ ** address of the OP_Gosub is stored in local variable addrGosubFlush. */
+ if( pMWin->pPartition ){
+ int addr;
+ ExprList *pPart = pMWin->pPartition;
+ int nPart = pPart->nExpr;
+ int regNewPart = regNew + pMWin->nBufferCol;
+ KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pPart, 0, 0);
+
+ regFlushPart = ++pParse->nMem;
+ addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, pMWin->regPart, nPart);
+ sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
+ sqlite3VdbeAddOp3(v, OP_Jump, addr+2, addr+4, addr+2);
+ VdbeCoverageEqNe(v);
+ addrGosubFlush = sqlite3VdbeAddOp1(v, OP_Gosub, regFlushPart);
+ VdbeComment((v, "call flush_partition"));
+ sqlite3VdbeAddOp3(v, OP_Copy, regNewPart, pMWin->regPart, nPart-1);
+ }
+
+ /* Insert the new row into the ephemeral table */
+ sqlite3VdbeAddOp2(v, OP_NewRowid, csrWrite, regRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, csrWrite, regRecord, regRowid);
+ addrNe = sqlite3VdbeAddOp3(v, OP_Ne, pMWin->regOne, 0, regRowid);
+ VdbeCoverageNeverNull(v);
+
+ /* This block is run for the first row of each partition */
+ s.regArg = windowInitAccum(pParse, pMWin);
+
+ if( regStart ){
+ sqlite3ExprCode(pParse, pMWin->pStart, regStart);
+ windowCheckValue(pParse, regStart, 0 + (pMWin->eFrmType==TK_RANGE ? 3 : 0));
+ }
+ if( regEnd ){
+ sqlite3ExprCode(pParse, pMWin->pEnd, regEnd);
+ windowCheckValue(pParse, regEnd, 1 + (pMWin->eFrmType==TK_RANGE ? 3 : 0));
+ }
+
+ if( pMWin->eStart==pMWin->eEnd && regStart ){
+ int op = ((pMWin->eStart==TK_FOLLOWING) ? OP_Ge : OP_Le);
+ int addrGe = sqlite3VdbeAddOp3(v, op, regStart, 0, regEnd);
+ VdbeCoverageNeverNullIf(v, op==OP_Ge); /* NeverNull because bound <expr> */
+ VdbeCoverageNeverNullIf(v, op==OP_Le); /* values previously checked */
+ windowAggFinal(&s, 0);
+ sqlite3VdbeAddOp2(v, OP_Rewind, s.current.csr, 1);
+ VdbeCoverageNeverTaken(v);
+ windowReturnOneRow(&s);
+ sqlite3VdbeAddOp1(v, OP_ResetSorter, s.current.csr);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, lblWhereEnd);
+ sqlite3VdbeJumpHere(v, addrGe);
+ }
+ if( pMWin->eStart==TK_FOLLOWING && pMWin->eFrmType!=TK_RANGE && regEnd ){
+ assert( pMWin->eEnd==TK_FOLLOWING );
+ sqlite3VdbeAddOp3(v, OP_Subtract, regStart, regEnd, regStart);
+ }
+
+ if( pMWin->eStart!=TK_UNBOUNDED ){
+ sqlite3VdbeAddOp2(v, OP_Rewind, s.start.csr, 1);
+ VdbeCoverageNeverTaken(v);
+ }
+ sqlite3VdbeAddOp2(v, OP_Rewind, s.current.csr, 1);
+ VdbeCoverageNeverTaken(v);
+ sqlite3VdbeAddOp2(v, OP_Rewind, s.end.csr, 1);
+ VdbeCoverageNeverTaken(v);
+ if( regPeer && pOrderBy ){
+ sqlite3VdbeAddOp3(v, OP_Copy, regNewPeer, regPeer, pOrderBy->nExpr-1);
+ sqlite3VdbeAddOp3(v, OP_Copy, regPeer, s.start.reg, pOrderBy->nExpr-1);
+ sqlite3VdbeAddOp3(v, OP_Copy, regPeer, s.current.reg, pOrderBy->nExpr-1);
+ sqlite3VdbeAddOp3(v, OP_Copy, regPeer, s.end.reg, pOrderBy->nExpr-1);
+ }
+
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, lblWhereEnd);
+
+ sqlite3VdbeJumpHere(v, addrNe);
+
+ /* Beginning of the block executed for the second and subsequent rows. */
+ if( regPeer ){
+ windowIfNewPeer(pParse, pOrderBy, regNewPeer, regPeer, lblWhereEnd);
+ }
+ if( pMWin->eStart==TK_FOLLOWING ){
+ windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0);
+ if( pMWin->eEnd!=TK_UNBOUNDED ){
+ if( pMWin->eFrmType==TK_RANGE ){
+ int lbl = sqlite3VdbeMakeLabel(pParse);
+ int addrNext = sqlite3VdbeCurrentAddr(v);
+ windowCodeRangeTest(&s, OP_Ge, s.current.csr, regEnd, s.end.csr, lbl);
+ windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
+ windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNext);
+ sqlite3VdbeResolveLabel(v, lbl);
+ }else{
+ windowCodeOp(&s, WINDOW_RETURN_ROW, regEnd, 0);
+ windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
+ }
}
+ }else
+ if( pMWin->eEnd==TK_PRECEDING ){
+ int bRPS = (pMWin->eStart==TK_PRECEDING && pMWin->eFrmType==TK_RANGE);
+ windowCodeOp(&s, WINDOW_AGGSTEP, regEnd, 0);
+ if( bRPS ) windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
+ windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
+ if( !bRPS ) windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
+ }else{
+ int addr = 0;
+ windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0);
+ if( pMWin->eEnd!=TK_UNBOUNDED ){
+ if( pMWin->eFrmType==TK_RANGE ){
+ int lbl = 0;
+ addr = sqlite3VdbeCurrentAddr(v);
+ if( regEnd ){
+ lbl = sqlite3VdbeMakeLabel(pParse);
+ windowCodeRangeTest(&s, OP_Ge, s.current.csr, regEnd, s.end.csr, lbl);
+ }
+ windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
+ windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
+ if( regEnd ){
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addr);
+ sqlite3VdbeResolveLabel(v, lbl);
+ }
+ }else{
+ if( regEnd ){
+ addr = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0, 1);
+ VdbeCoverage(v);
+ }
+ windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
+ windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
+ if( regEnd ) sqlite3VdbeJumpHere(v, addr);
+ }
+ }
+ }
- /* Otherwise, call windowCodeDefaultStep(). */
- if( bCache ){
- VdbeModuleComment((pParse->pVdbe, "Begin CacheStep()"));
- windowCodeCacheStep(pParse, p, pWInfo, regGosub, addrGosub);
- }else{
- VdbeModuleComment((pParse->pVdbe, "Begin DefaultStep()"));
- windowCodeDefaultStep(pParse, p, pWInfo, regGosub, addrGosub);
+ /* End of the main input loop */
+ sqlite3VdbeResolveLabel(v, lblWhereEnd);
+ sqlite3WhereEnd(pWInfo);
+
+ /* Fall through */
+ if( pMWin->pPartition ){
+ addrInteger = sqlite3VdbeAddOp2(v, OP_Integer, 0, regFlushPart);
+ sqlite3VdbeJumpHere(v, addrGosubFlush);
+ }
+
+ addrEmpty = sqlite3VdbeAddOp1(v, OP_Rewind, csrWrite);
+ VdbeCoverage(v);
+ if( pMWin->eEnd==TK_PRECEDING ){
+ int bRPS = (pMWin->eStart==TK_PRECEDING && pMWin->eFrmType==TK_RANGE);
+ windowCodeOp(&s, WINDOW_AGGSTEP, regEnd, 0);
+ if( bRPS ) windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
+ windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
+ }else if( pMWin->eStart==TK_FOLLOWING ){
+ int addrStart;
+ int addrBreak1;
+ int addrBreak2;
+ int addrBreak3;
+ windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0);
+ if( pMWin->eFrmType==TK_RANGE ){
+ addrStart = sqlite3VdbeCurrentAddr(v);
+ addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 1);
+ addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 1);
+ }else
+ if( pMWin->eEnd==TK_UNBOUNDED ){
+ addrStart = sqlite3VdbeCurrentAddr(v);
+ addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, regStart, 1);
+ addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, 0, 1);
+ }else{
+ assert( pMWin->eEnd==TK_FOLLOWING );
+ addrStart = sqlite3VdbeCurrentAddr(v);
+ addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, regEnd, 1);
+ addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 1);
+ }
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart);
+ sqlite3VdbeJumpHere(v, addrBreak2);
+ addrStart = sqlite3VdbeCurrentAddr(v);
+ addrBreak3 = windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 1);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart);
+ sqlite3VdbeJumpHere(v, addrBreak1);
+ sqlite3VdbeJumpHere(v, addrBreak3);
+ }else{
+ int addrBreak;
+ int addrStart;
+ windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0);
+ addrStart = sqlite3VdbeCurrentAddr(v);
+ addrBreak = windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 1);
+ windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart);
+ sqlite3VdbeJumpHere(v, addrBreak);
+ }
+ sqlite3VdbeJumpHere(v, addrEmpty);
+
+ sqlite3VdbeAddOp1(v, OP_ResetSorter, s.current.csr);
+ if( pMWin->pPartition ){
+ if( pMWin->regStartRowid ){
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, pMWin->regStartRowid);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, pMWin->regEndRowid);
}
+ sqlite3VdbeChangeP1(v, addrInteger, sqlite3VdbeCurrentAddr(v));
+ sqlite3VdbeAddOp1(v, OP_Return, regFlushPart);
}
}
@@ -147500,6 +148298,10 @@ static void disableLookaside(Parse *pParse){
sqlite3ExprListSetName(pParse, p, pIdToken, 1);
return p;
}
+
+#if TK_SPAN>255
+# error too many tokens in the grammar
+#endif
/**************** End of %include directives **********************************/
/* These constants specify the various numeric values for terminal symbols
** in a format understandable to "makeheaders". This section is blank unless
@@ -147563,27 +148365,28 @@ static void disableLookaside(Parse *pParse){
#endif
/************* Begin control #defines *****************************************/
#define YYCODETYPE unsigned short int
-#define YYNOCODE 278
+#define YYNOCODE 301
#define YYACTIONTYPE unsigned short int
-#define YYWILDCARD 91
+#define YYWILDCARD 95
#define sqlite3ParserTOKENTYPE Token
typedef union {
int yyinit;
sqlite3ParserTOKENTYPE yy0;
- ExprList* yy42;
- int yy96;
- TriggerStep* yy119;
- Window* yy147;
- SrcList* yy167;
- Upsert* yy266;
- struct FrameBound yy317;
- IdList* yy336;
- struct TrigEvent yy350;
- struct {int value; int mask;} yy367;
- Select* yy423;
- const char* yy464;
- Expr* yy490;
- With* yy499;
+ With* yy59;
+ IdList* yy62;
+ struct TrigEvent yy90;
+ Upsert* yy136;
+ struct FrameBound yy201;
+ u8 yy238;
+ const char* yy294;
+ Window* yy295;
+ struct {int value; int mask;} yy355;
+ ExprList* yy434;
+ TriggerStep* yy455;
+ Select* yy457;
+ SrcList* yy483;
+ int yy494;
+ Expr* yy524;
} YYMINORTYPE;
#ifndef YYSTACKDEPTH
#define YYSTACKDEPTH 100
@@ -147599,17 +148402,17 @@ typedef union {
#define sqlite3ParserCTX_FETCH Parse *pParse=yypParser->pParse;
#define sqlite3ParserCTX_STORE yypParser->pParse=pParse;
#define YYFALLBACK 1
-#define YYNSTATE 524
-#define YYNRULE 369
-#define YYNTOKEN 155
-#define YY_MAX_SHIFT 523
-#define YY_MIN_SHIFTREDUCE 760
-#define YY_MAX_SHIFTREDUCE 1128
-#define YY_ERROR_ACTION 1129
-#define YY_ACCEPT_ACTION 1130
-#define YY_NO_ACTION 1131
-#define YY_MIN_REDUCE 1132
-#define YY_MAX_REDUCE 1500
+#define YYNSTATE 541
+#define YYNRULE 375
+#define YYNTOKEN 176
+#define YY_MAX_SHIFT 540
+#define YY_MIN_SHIFTREDUCE 784
+#define YY_MAX_SHIFTREDUCE 1158
+#define YY_ERROR_ACTION 1159
+#define YY_ACCEPT_ACTION 1160
+#define YY_NO_ACTION 1161
+#define YY_MIN_REDUCE 1162
+#define YY_MAX_REDUCE 1536
/************* End control #defines *******************************************/
#define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])))
@@ -147676,569 +148479,603 @@ typedef union {
** yy_default[] Default action for each state.
**
*********** Begin parsing tables **********************************************/
-#define YY_ACTTAB_COUNT (2009)
+#define YY_ACTTAB_COUNT (2142)
static const YYACTIONTYPE yy_action[] = {
- /* 0 */ 377, 518, 371, 107, 104, 200, 1293, 518, 1130, 1,
- /* 10 */ 1, 523, 2, 1134, 518, 1203, 1203, 1262, 277, 373,
- /* 20 */ 129, 495, 37, 37, 1397, 1201, 1201, 1211, 65, 65,
- /* 30 */ 480, 891, 107, 104, 200, 37, 37, 1043, 1494, 892,
- /* 40 */ 346, 1494, 342, 114, 115, 105, 1106, 1106, 957, 960,
- /* 50 */ 950, 950, 112, 112, 113, 113, 113, 113, 285, 254,
- /* 60 */ 254, 518, 254, 254, 500, 518, 495, 518, 107, 104,
- /* 70 */ 200, 1085, 515, 481, 386, 515, 1464, 442, 501, 230,
- /* 80 */ 197, 439, 37, 37, 1172, 210, 65, 65, 65, 65,
- /* 90 */ 254, 254, 111, 111, 111, 111, 110, 110, 109, 109,
- /* 100 */ 109, 108, 404, 515, 404, 155, 1041, 431, 401, 400,
- /* 110 */ 254, 254, 373, 1431, 1427, 408, 1110, 1085, 1086, 1087,
- /* 120 */ 284, 1112, 500, 515, 500, 368, 1433, 1421, 1428, 1111,
- /* 130 */ 1261, 499, 373, 502, 108, 404, 114, 115, 105, 1106,
- /* 140 */ 1106, 957, 960, 950, 950, 112, 112, 113, 113, 113,
- /* 150 */ 113, 276, 509, 1113, 369, 1113, 114, 115, 105, 1106,
- /* 160 */ 1106, 957, 960, 950, 950, 112, 112, 113, 113, 113,
- /* 170 */ 113, 496, 1420, 1431, 493, 1468, 1065, 260, 1063, 433,
- /* 180 */ 74, 107, 104, 200, 498, 111, 111, 111, 111, 110,
- /* 190 */ 110, 109, 109, 109, 108, 404, 373, 113, 113, 113,
- /* 200 */ 113, 106, 131, 91, 1361, 111, 111, 111, 111, 110,
- /* 210 */ 110, 109, 109, 109, 108, 404, 113, 113, 113, 113,
- /* 220 */ 114, 115, 105, 1106, 1106, 957, 960, 950, 950, 112,
- /* 230 */ 112, 113, 113, 113, 113, 111, 111, 111, 111, 110,
- /* 240 */ 110, 109, 109, 109, 108, 404, 116, 110, 110, 109,
- /* 250 */ 109, 109, 108, 404, 111, 111, 111, 111, 110, 110,
- /* 260 */ 109, 109, 109, 108, 404, 917, 512, 512, 512, 111,
- /* 270 */ 111, 111, 111, 110, 110, 109, 109, 109, 108, 404,
- /* 280 */ 517, 1198, 1177, 181, 109, 109, 109, 108, 404, 373,
- /* 290 */ 1198, 402, 402, 402, 75, 360, 111, 111, 111, 111,
- /* 300 */ 110, 110, 109, 109, 109, 108, 404, 382, 299, 419,
- /* 310 */ 287, 170, 518, 114, 115, 105, 1106, 1106, 957, 960,
- /* 320 */ 950, 950, 112, 112, 113, 113, 113, 113, 1444, 523,
- /* 330 */ 2, 1134, 518, 13, 13, 337, 277, 1085, 129, 226,
- /* 340 */ 937, 1058, 1000, 471, 917, 1211, 453, 384, 1085, 395,
- /* 350 */ 162, 1057, 155, 45, 45, 416, 928, 401, 400, 479,
- /* 360 */ 927, 12, 111, 111, 111, 111, 110, 110, 109, 109,
- /* 370 */ 109, 108, 404, 226, 286, 254, 254, 254, 254, 518,
- /* 380 */ 16, 16, 373, 1085, 1086, 1087, 314, 299, 515, 472,
- /* 390 */ 515, 927, 927, 929, 1085, 1086, 1087, 378, 276, 509,
- /* 400 */ 65, 65, 1113, 210, 1113, 1085, 114, 115, 105, 1106,
- /* 410 */ 1106, 957, 960, 950, 950, 112, 112, 113, 113, 113,
- /* 420 */ 113, 1448, 222, 1134, 1089, 461, 458, 457, 277, 180,
- /* 430 */ 129, 378, 392, 408, 423, 456, 500, 1211, 240, 257,
- /* 440 */ 324, 464, 319, 463, 227, 470, 12, 317, 424, 300,
- /* 450 */ 317, 1085, 1086, 1087, 485, 111, 111, 111, 111, 110,
- /* 460 */ 110, 109, 109, 109, 108, 404, 181, 118, 1085, 254,
- /* 470 */ 254, 1089, 518, 90, 351, 373, 518, 1181, 365, 798,
- /* 480 */ 1440, 339, 515, 248, 248, 77, 325, 133, 1085, 249,
- /* 490 */ 424, 300, 794, 49, 49, 210, 515, 65, 65, 114,
- /* 500 */ 115, 105, 1106, 1106, 957, 960, 950, 950, 112, 112,
- /* 510 */ 113, 113, 113, 113, 1085, 1086, 1087, 222, 1085, 438,
- /* 520 */ 461, 458, 457, 937, 787, 408, 171, 857, 362, 1021,
- /* 530 */ 456, 136, 198, 486, 1085, 1086, 1087, 448, 794, 928,
- /* 540 */ 5, 193, 192, 927, 1022, 107, 104, 200, 111, 111,
- /* 550 */ 111, 111, 110, 110, 109, 109, 109, 108, 404, 1023,
- /* 560 */ 254, 254, 803, 1085, 1085, 1086, 1087, 437, 373, 1085,
- /* 570 */ 344, 787, 791, 515, 927, 927, 929, 1085, 1408, 1396,
- /* 580 */ 832, 1085, 176, 3, 852, 1085, 518, 1439, 429, 851,
- /* 590 */ 833, 518, 114, 115, 105, 1106, 1106, 957, 960, 950,
- /* 600 */ 950, 112, 112, 113, 113, 113, 113, 13, 13, 1085,
- /* 610 */ 1086, 1087, 13, 13, 518, 1085, 1086, 1087, 1496, 358,
- /* 620 */ 1085, 389, 1234, 1085, 1086, 1087, 391, 1085, 1086, 1087,
- /* 630 */ 448, 1085, 1086, 1087, 518, 65, 65, 947, 947, 958,
- /* 640 */ 961, 111, 111, 111, 111, 110, 110, 109, 109, 109,
- /* 650 */ 108, 404, 518, 382, 878, 13, 13, 518, 877, 518,
- /* 660 */ 263, 373, 518, 431, 448, 1070, 1085, 1086, 1087, 267,
- /* 670 */ 448, 488, 1360, 64, 64, 431, 812, 155, 50, 50,
- /* 680 */ 65, 65, 518, 65, 65, 114, 115, 105, 1106, 1106,
- /* 690 */ 957, 960, 950, 950, 112, 112, 113, 113, 113, 113,
- /* 700 */ 518, 951, 382, 13, 13, 415, 411, 462, 414, 1085,
- /* 710 */ 1366, 777, 1210, 292, 297, 813, 399, 497, 181, 403,
- /* 720 */ 261, 15, 15, 276, 509, 414, 413, 1366, 1368, 410,
- /* 730 */ 372, 345, 1209, 264, 111, 111, 111, 111, 110, 110,
- /* 740 */ 109, 109, 109, 108, 404, 265, 254, 254, 229, 1405,
- /* 750 */ 268, 1215, 268, 1103, 373, 1085, 1086, 1087, 938, 515,
- /* 760 */ 393, 409, 876, 515, 254, 254, 1152, 482, 473, 262,
- /* 770 */ 422, 476, 325, 503, 289, 518, 291, 515, 114, 115,
- /* 780 */ 105, 1106, 1106, 957, 960, 950, 950, 112, 112, 113,
- /* 790 */ 113, 113, 113, 414, 1021, 1366, 39, 39, 254, 254,
- /* 800 */ 254, 254, 980, 254, 254, 254, 254, 255, 255, 1022,
- /* 810 */ 279, 515, 516, 515, 846, 846, 515, 138, 515, 518,
- /* 820 */ 515, 1043, 1495, 251, 1023, 1495, 876, 111, 111, 111,
- /* 830 */ 111, 110, 110, 109, 109, 109, 108, 404, 518, 1353,
- /* 840 */ 51, 51, 518, 199, 518, 506, 290, 373, 518, 276,
- /* 850 */ 509, 922, 9, 483, 233, 1005, 1005, 445, 189, 52,
- /* 860 */ 52, 325, 280, 53, 53, 54, 54, 373, 876, 55,
- /* 870 */ 55, 114, 115, 105, 1106, 1106, 957, 960, 950, 950,
- /* 880 */ 112, 112, 113, 113, 113, 113, 97, 518, 95, 1104,
- /* 890 */ 1041, 114, 115, 105, 1106, 1106, 957, 960, 950, 950,
- /* 900 */ 112, 112, 113, 113, 113, 113, 135, 199, 56, 56,
- /* 910 */ 765, 766, 767, 225, 224, 223, 518, 283, 437, 233,
- /* 920 */ 111, 111, 111, 111, 110, 110, 109, 109, 109, 108,
- /* 930 */ 404, 1002, 876, 326, 518, 1002, 1104, 40, 40, 518,
- /* 940 */ 111, 111, 111, 111, 110, 110, 109, 109, 109, 108,
- /* 950 */ 404, 518, 448, 518, 1104, 41, 41, 518, 17, 518,
- /* 960 */ 43, 43, 1155, 379, 518, 448, 518, 443, 518, 390,
- /* 970 */ 518, 194, 44, 44, 57, 57, 1247, 518, 58, 58,
- /* 980 */ 59, 59, 518, 466, 326, 14, 14, 60, 60, 120,
- /* 990 */ 120, 61, 61, 449, 1206, 93, 518, 425, 46, 46,
- /* 1000 */ 518, 1104, 518, 62, 62, 518, 437, 305, 518, 852,
- /* 1010 */ 518, 298, 518, 1246, 851, 373, 518, 63, 63, 1293,
- /* 1020 */ 397, 47, 47, 142, 142, 1467, 143, 143, 821, 70,
- /* 1030 */ 70, 48, 48, 66, 66, 373, 518, 121, 121, 114,
- /* 1040 */ 115, 105, 1106, 1106, 957, 960, 950, 950, 112, 112,
- /* 1050 */ 113, 113, 113, 113, 518, 418, 518, 67, 67, 114,
- /* 1060 */ 115, 105, 1106, 1106, 957, 960, 950, 950, 112, 112,
- /* 1070 */ 113, 113, 113, 113, 312, 122, 122, 123, 123, 1293,
- /* 1080 */ 518, 357, 1126, 88, 518, 435, 325, 387, 111, 111,
- /* 1090 */ 111, 111, 110, 110, 109, 109, 109, 108, 404, 266,
- /* 1100 */ 518, 119, 119, 518, 1293, 141, 141, 518, 111, 111,
- /* 1110 */ 111, 111, 110, 110, 109, 109, 109, 108, 404, 518,
- /* 1120 */ 801, 140, 140, 518, 127, 127, 511, 379, 126, 126,
- /* 1130 */ 518, 137, 518, 1308, 518, 307, 518, 310, 518, 203,
- /* 1140 */ 124, 124, 1307, 96, 125, 125, 207, 388, 1441, 468,
- /* 1150 */ 1127, 69, 69, 71, 71, 68, 68, 38, 38, 42,
- /* 1160 */ 42, 357, 1042, 373, 1293, 276, 509, 801, 185, 469,
- /* 1170 */ 494, 436, 444, 6, 380, 156, 253, 197, 469, 134,
- /* 1180 */ 426, 33, 1038, 373, 1121, 359, 1411, 114, 115, 105,
- /* 1190 */ 1106, 1106, 957, 960, 950, 950, 112, 112, 113, 113,
- /* 1200 */ 113, 113, 914, 296, 27, 293, 90, 114, 103, 105,
- /* 1210 */ 1106, 1106, 957, 960, 950, 950, 112, 112, 113, 113,
- /* 1220 */ 113, 113, 919, 275, 430, 232, 891, 232, 432, 256,
- /* 1230 */ 1127, 232, 398, 370, 892, 28, 111, 111, 111, 111,
- /* 1240 */ 110, 110, 109, 109, 109, 108, 404, 301, 454, 1385,
- /* 1250 */ 90, 228, 209, 987, 811, 810, 111, 111, 111, 111,
- /* 1260 */ 110, 110, 109, 109, 109, 108, 404, 315, 818, 819,
- /* 1270 */ 90, 323, 983, 931, 885, 228, 373, 232, 999, 849,
- /* 1280 */ 999, 322, 102, 998, 1384, 998, 785, 850, 440, 132,
- /* 1290 */ 102, 302, 1243, 306, 309, 311, 373, 313, 1194, 1180,
- /* 1300 */ 987, 115, 105, 1106, 1106, 957, 960, 950, 950, 112,
- /* 1310 */ 112, 113, 113, 113, 113, 1178, 1179, 318, 327, 328,
- /* 1320 */ 931, 1255, 105, 1106, 1106, 957, 960, 950, 950, 112,
- /* 1330 */ 112, 113, 113, 113, 113, 1292, 1230, 1457, 273, 1241,
- /* 1340 */ 504, 505, 1298, 100, 510, 246, 4, 1161, 1154, 111,
- /* 1350 */ 111, 111, 111, 110, 110, 109, 109, 109, 108, 404,
- /* 1360 */ 513, 1143, 187, 1142, 202, 1144, 1451, 356, 1227, 111,
- /* 1370 */ 111, 111, 111, 110, 110, 109, 109, 109, 108, 404,
- /* 1380 */ 11, 1277, 330, 405, 332, 334, 191, 1285, 364, 195,
- /* 1390 */ 295, 417, 288, 100, 510, 507, 4, 434, 459, 321,
- /* 1400 */ 1177, 349, 1357, 1356, 336, 155, 190, 1454, 1121, 158,
- /* 1410 */ 513, 508, 235, 1404, 937, 1402, 1118, 381, 77, 428,
- /* 1420 */ 98, 98, 8, 1282, 168, 30, 152, 99, 160, 405,
- /* 1430 */ 520, 519, 88, 405, 927, 1362, 1274, 420, 163, 73,
- /* 1440 */ 164, 76, 165, 166, 421, 507, 452, 212, 361, 363,
- /* 1450 */ 427, 276, 509, 31, 1288, 172, 491, 441, 216, 1351,
- /* 1460 */ 82, 490, 447, 1373, 937, 927, 927, 929, 930, 24,
- /* 1470 */ 98, 98, 304, 247, 218, 177, 308, 99, 219, 405,
- /* 1480 */ 520, 519, 450, 1145, 927, 220, 366, 1197, 100, 510,
- /* 1490 */ 465, 4, 1188, 1196, 1195, 394, 803, 1169, 1187, 367,
- /* 1500 */ 1168, 396, 484, 320, 1167, 513, 1466, 87, 475, 100,
- /* 1510 */ 510, 271, 4, 272, 478, 927, 927, 929, 930, 24,
- /* 1520 */ 1443, 1074, 407, 1238, 1239, 258, 513, 329, 405, 331,
- /* 1530 */ 355, 355, 354, 243, 352, 234, 489, 774, 498, 184,
- /* 1540 */ 507, 338, 1422, 339, 117, 1220, 10, 341, 333, 405,
- /* 1550 */ 204, 491, 282, 1219, 1237, 1236, 492, 335, 343, 937,
- /* 1560 */ 281, 507, 94, 1337, 186, 98, 98, 347, 89, 487,
- /* 1570 */ 348, 241, 99, 29, 405, 520, 519, 274, 1151, 927,
- /* 1580 */ 937, 521, 1080, 245, 242, 244, 98, 98, 856, 522,
- /* 1590 */ 206, 1140, 1135, 99, 144, 405, 520, 519, 147, 375,
- /* 1600 */ 927, 149, 376, 157, 1389, 1390, 1388, 1387, 205, 145,
- /* 1610 */ 927, 927, 929, 930, 24, 146, 130, 761, 1165, 1164,
- /* 1620 */ 72, 100, 510, 1162, 4, 269, 406, 188, 278, 201,
- /* 1630 */ 259, 927, 927, 929, 930, 24, 128, 911, 513, 997,
- /* 1640 */ 995, 159, 374, 208, 148, 161, 835, 276, 509, 211,
- /* 1650 */ 294, 1011, 915, 167, 150, 383, 169, 78, 385, 79,
- /* 1660 */ 80, 405, 81, 151, 1014, 213, 214, 1010, 139, 18,
- /* 1670 */ 412, 215, 303, 507, 232, 1115, 1003, 446, 173, 217,
- /* 1680 */ 174, 32, 776, 451, 491, 322, 221, 175, 814, 490,
- /* 1690 */ 83, 455, 937, 19, 460, 316, 20, 84, 98, 98,
- /* 1700 */ 270, 182, 85, 467, 153, 99, 154, 405, 520, 519,
- /* 1710 */ 1074, 407, 927, 183, 258, 963, 1046, 86, 34, 355,
- /* 1720 */ 355, 354, 243, 352, 474, 1047, 774, 35, 477, 196,
- /* 1730 */ 250, 100, 510, 252, 4, 884, 178, 231, 1060, 204,
- /* 1740 */ 21, 282, 102, 927, 927, 929, 930, 24, 513, 281,
- /* 1750 */ 879, 22, 1064, 1062, 1051, 7, 340, 23, 978, 179,
- /* 1760 */ 90, 92, 510, 964, 4, 236, 962, 966, 1020, 1019,
- /* 1770 */ 237, 405, 967, 25, 36, 514, 932, 786, 513, 206,
- /* 1780 */ 101, 26, 845, 507, 238, 239, 1459, 147, 350, 1458,
- /* 1790 */ 149, 353, 1075, 1131, 1131, 1131, 1131, 205, 1131, 1131,
- /* 1800 */ 1131, 405, 937, 1131, 1131, 1131, 1131, 1131, 98, 98,
- /* 1810 */ 1131, 1131, 1131, 507, 1131, 99, 1131, 405, 520, 519,
- /* 1820 */ 1131, 1131, 927, 1131, 1131, 1131, 1131, 1131, 1131, 1131,
- /* 1830 */ 1131, 374, 937, 1131, 1131, 1131, 276, 509, 98, 98,
- /* 1840 */ 1131, 1131, 1131, 1131, 1131, 99, 1131, 405, 520, 519,
- /* 1850 */ 1131, 1131, 927, 927, 927, 929, 930, 24, 1131, 412,
- /* 1860 */ 1131, 1131, 1131, 258, 1131, 1131, 1131, 1131, 355, 355,
- /* 1870 */ 354, 243, 352, 1131, 1131, 774, 1131, 1131, 1131, 1131,
- /* 1880 */ 1131, 1131, 1131, 927, 927, 929, 930, 24, 204, 1131,
- /* 1890 */ 282, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 281, 1131,
- /* 1900 */ 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131,
- /* 1910 */ 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131,
- /* 1920 */ 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 206, 1131,
- /* 1930 */ 1131, 1131, 1131, 1131, 1131, 1131, 147, 1131, 1131, 149,
- /* 1940 */ 1131, 1131, 1131, 1131, 1131, 1131, 205, 1131, 1131, 1131,
- /* 1950 */ 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131,
- /* 1960 */ 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131,
- /* 1970 */ 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131,
- /* 1980 */ 374, 1131, 1131, 1131, 1131, 276, 509, 1131, 1131, 1131,
- /* 1990 */ 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131,
- /* 2000 */ 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 412,
+ /* 0 */ 535, 1323, 112, 109, 209, 112, 109, 209, 1160, 1,
+ /* 10 */ 1, 540, 2, 1164, 535, 1292, 1228, 1207, 289, 384,
+ /* 20 */ 134, 42, 42, 1427, 382, 1228, 9, 1241, 242, 492,
+ /* 30 */ 1291, 915, 373, 379, 1026, 70, 70, 427, 1026, 916,
+ /* 40 */ 529, 529, 529, 119, 120, 110, 1136, 1136, 981, 984,
+ /* 50 */ 974, 974, 117, 117, 118, 118, 118, 118, 380, 264,
+ /* 60 */ 264, 264, 264, 1134, 264, 264, 112, 109, 209, 397,
+ /* 70 */ 454, 517, 532, 491, 532, 1233, 1233, 532, 239, 206,
+ /* 80 */ 493, 112, 109, 209, 464, 219, 118, 118, 118, 118,
+ /* 90 */ 111, 393, 440, 444, 16, 16, 116, 116, 116, 116,
+ /* 100 */ 115, 115, 114, 114, 114, 113, 415, 971, 971, 982,
+ /* 110 */ 985, 235, 1463, 351, 1134, 419, 384, 116, 116, 116,
+ /* 120 */ 116, 115, 115, 114, 114, 114, 113, 415, 116, 116,
+ /* 130 */ 116, 116, 115, 115, 114, 114, 114, 113, 415, 961,
+ /* 140 */ 119, 120, 110, 1136, 1136, 981, 984, 974, 974, 117,
+ /* 150 */ 117, 118, 118, 118, 118, 952, 415, 941, 298, 951,
+ /* 160 */ 941, 1480, 540, 2, 1164, 1115, 535, 1458, 160, 289,
+ /* 170 */ 6, 134, 1504, 389, 406, 975, 338, 1024, 1241, 337,
+ /* 180 */ 1089, 1476, 1089, 118, 118, 118, 118, 42, 42, 329,
+ /* 190 */ 951, 951, 953, 116, 116, 116, 116, 115, 115, 114,
+ /* 200 */ 114, 114, 113, 415, 311, 430, 299, 311, 881, 160,
+ /* 210 */ 264, 264, 401, 384, 324, 1115, 1116, 1117, 288, 526,
+ /* 220 */ 96, 159, 1441, 532, 141, 116, 116, 116, 116, 115,
+ /* 230 */ 115, 114, 114, 114, 113, 415, 219, 119, 120, 110,
+ /* 240 */ 1136, 1136, 981, 984, 974, 974, 117, 117, 118, 118,
+ /* 250 */ 118, 118, 115, 115, 114, 114, 114, 113, 415, 288,
+ /* 260 */ 526, 403, 533, 121, 870, 870, 419, 250, 267, 336,
+ /* 270 */ 475, 331, 474, 236, 160, 319, 1084, 322, 1465, 329,
+ /* 280 */ 350, 12, 535, 384, 502, 1115, 1084, 435, 312, 1084,
+ /* 290 */ 116, 116, 116, 116, 115, 115, 114, 114, 114, 113,
+ /* 300 */ 415, 535, 836, 42, 42, 138, 426, 119, 120, 110,
+ /* 310 */ 1136, 1136, 981, 984, 974, 974, 117, 117, 118, 118,
+ /* 320 */ 118, 118, 70, 70, 288, 526, 412, 411, 480, 1457,
+ /* 330 */ 335, 79, 6, 473, 1140, 1115, 1116, 1117, 501, 1142,
+ /* 340 */ 334, 837, 811, 1484, 512, 1164, 534, 1141, 123, 187,
+ /* 350 */ 289, 384, 134, 448, 434, 1115, 80, 349, 498, 1241,
+ /* 360 */ 116, 116, 116, 116, 115, 115, 114, 114, 114, 113,
+ /* 370 */ 415, 1143, 1115, 1143, 459, 119, 120, 110, 1136, 1136,
+ /* 380 */ 981, 984, 974, 974, 117, 117, 118, 118, 118, 118,
+ /* 390 */ 404, 264, 264, 811, 1463, 506, 368, 1156, 535, 114,
+ /* 400 */ 114, 114, 113, 415, 532, 1115, 1116, 1117, 231, 518,
+ /* 410 */ 1500, 472, 469, 468, 175, 497, 422, 219, 1202, 70,
+ /* 420 */ 70, 467, 1115, 1116, 1117, 176, 201, 200, 116, 116,
+ /* 430 */ 116, 116, 115, 115, 114, 114, 114, 113, 415, 535,
+ /* 440 */ 1115, 264, 264, 435, 312, 1115, 273, 419, 384, 513,
+ /* 450 */ 1450, 1115, 326, 1084, 532, 517, 82, 1084, 167, 388,
+ /* 460 */ 69, 69, 1115, 1084, 519, 509, 1084, 1084, 12, 1157,
+ /* 470 */ 1084, 420, 119, 120, 110, 1136, 1136, 981, 984, 974,
+ /* 480 */ 974, 117, 117, 118, 118, 118, 118, 258, 258, 535,
+ /* 490 */ 1115, 1116, 1117, 1045, 535, 1115, 1116, 1117, 1323, 535,
+ /* 500 */ 532, 1115, 1116, 1117, 296, 483, 1211, 818, 1046, 448,
+ /* 510 */ 70, 70, 1115, 1116, 1117, 50, 50, 448, 356, 500,
+ /* 520 */ 70, 70, 207, 1047, 32, 116, 116, 116, 116, 115,
+ /* 530 */ 115, 114, 114, 114, 113, 415, 453, 264, 264, 1115,
+ /* 540 */ 450, 449, 961, 508, 856, 384, 517, 5, 900, 822,
+ /* 550 */ 532, 484, 181, 1115, 857, 516, 517, 818, 952, 507,
+ /* 560 */ 3, 1115, 951, 1231, 1231, 482, 398, 1115, 1095, 119,
+ /* 570 */ 120, 110, 1136, 1136, 981, 984, 974, 974, 117, 117,
+ /* 580 */ 118, 118, 118, 118, 1115, 535, 238, 1115, 1391, 1115,
+ /* 590 */ 1116, 1117, 159, 951, 951, 953, 231, 1115, 259, 472,
+ /* 600 */ 469, 468, 310, 1115, 1116, 1117, 13, 13, 297, 467,
+ /* 610 */ 276, 1115, 1116, 1117, 412, 411, 1095, 1115, 1116, 1117,
+ /* 620 */ 395, 355, 116, 116, 116, 116, 115, 115, 114, 114,
+ /* 630 */ 114, 113, 415, 208, 1115, 1116, 1117, 1115, 1116, 1117,
+ /* 640 */ 264, 264, 384, 337, 902, 393, 815, 1115, 1116, 1117,
+ /* 650 */ 413, 413, 413, 532, 112, 109, 209, 309, 900, 1143,
+ /* 660 */ 535, 1143, 535, 393, 901, 1210, 119, 120, 110, 1136,
+ /* 670 */ 1136, 981, 984, 974, 974, 117, 117, 118, 118, 118,
+ /* 680 */ 118, 13, 13, 13, 13, 265, 265, 535, 143, 264,
+ /* 690 */ 264, 288, 526, 535, 1119, 400, 535, 402, 532, 510,
+ /* 700 */ 1457, 512, 532, 6, 113, 415, 1067, 1530, 70, 70,
+ /* 710 */ 1530, 535, 271, 535, 70, 70, 535, 13, 13, 116,
+ /* 720 */ 116, 116, 116, 115, 115, 114, 114, 114, 113, 415,
+ /* 730 */ 272, 277, 13, 13, 13, 13, 535, 13, 13, 384,
+ /* 740 */ 535, 304, 425, 1100, 284, 1119, 184, 801, 185, 338,
+ /* 750 */ 285, 514, 1532, 369, 1239, 1438, 1182, 70, 70, 425,
+ /* 760 */ 424, 70, 70, 119, 120, 110, 1136, 1136, 981, 984,
+ /* 770 */ 974, 974, 117, 117, 118, 118, 118, 118, 190, 1065,
+ /* 780 */ 1067, 1531, 442, 107, 1531, 408, 264, 264, 264, 264,
+ /* 790 */ 383, 1396, 261, 410, 95, 900, 485, 414, 421, 532,
+ /* 800 */ 1045, 532, 301, 1133, 303, 488, 433, 1451, 1396, 1398,
+ /* 810 */ 278, 535, 278, 520, 1435, 1046, 116, 116, 116, 116,
+ /* 820 */ 115, 115, 114, 114, 114, 113, 415, 425, 264, 264,
+ /* 830 */ 1047, 190, 54, 54, 535, 291, 384, 264, 264, 362,
+ /* 840 */ 962, 532, 1004, 376, 1084, 264, 264, 1029, 1029, 456,
+ /* 850 */ 532, 523, 270, 1065, 1084, 55, 55, 1084, 532, 442,
+ /* 860 */ 119, 120, 110, 1136, 1136, 981, 984, 974, 974, 117,
+ /* 870 */ 117, 118, 118, 118, 118, 535, 1396, 190, 302, 1383,
+ /* 880 */ 208, 535, 789, 790, 791, 535, 515, 535, 1323, 371,
+ /* 890 */ 337, 234, 233, 232, 459, 515, 15, 15, 459, 477,
+ /* 900 */ 459, 459, 44, 44, 136, 900, 56, 56, 57, 57,
+ /* 910 */ 1185, 390, 197, 116, 116, 116, 116, 115, 115, 114,
+ /* 920 */ 114, 114, 113, 415, 535, 876, 535, 442, 535, 274,
+ /* 930 */ 875, 1323, 357, 384, 353, 140, 1426, 946, 1455, 1323,
+ /* 940 */ 1390, 6, 1240, 1236, 292, 58, 58, 59, 59, 60,
+ /* 950 */ 60, 535, 1456, 384, 535, 6, 399, 119, 120, 110,
+ /* 960 */ 1136, 1136, 981, 984, 974, 974, 117, 117, 118, 118,
+ /* 970 */ 118, 118, 61, 61, 535, 45, 45, 119, 120, 110,
+ /* 980 */ 1136, 1136, 981, 984, 974, 974, 117, 117, 118, 118,
+ /* 990 */ 118, 118, 1477, 479, 202, 46, 46, 275, 95, 455,
+ /* 1000 */ 535, 212, 535, 337, 535, 1454, 535, 409, 6, 242,
+ /* 1010 */ 116, 116, 116, 116, 115, 115, 114, 114, 114, 113,
+ /* 1020 */ 415, 48, 48, 49, 49, 62, 62, 63, 63, 535,
+ /* 1030 */ 116, 116, 116, 116, 115, 115, 114, 114, 114, 113,
+ /* 1040 */ 415, 535, 459, 535, 1134, 535, 1151, 535, 142, 535,
+ /* 1050 */ 64, 64, 535, 1338, 535, 494, 535, 446, 535, 1264,
+ /* 1060 */ 535, 1337, 14, 14, 65, 65, 125, 125, 66, 66,
+ /* 1070 */ 51, 51, 535, 67, 67, 68, 68, 52, 52, 147,
+ /* 1080 */ 147, 148, 148, 1453, 317, 98, 6, 535, 1245, 481,
+ /* 1090 */ 535, 827, 535, 75, 75, 1134, 102, 481, 100, 535,
+ /* 1100 */ 532, 535, 368, 1066, 1503, 384, 535, 845, 53, 53,
+ /* 1110 */ 93, 71, 71, 126, 126, 295, 528, 390, 288, 526,
+ /* 1120 */ 72, 72, 127, 127, 139, 384, 38, 128, 128, 119,
+ /* 1130 */ 120, 110, 1136, 1136, 981, 984, 974, 974, 117, 117,
+ /* 1140 */ 118, 118, 118, 118, 535, 495, 535, 447, 535, 119,
+ /* 1150 */ 120, 110, 1136, 1136, 981, 984, 974, 974, 117, 117,
+ /* 1160 */ 118, 118, 118, 118, 235, 124, 124, 146, 146, 145,
+ /* 1170 */ 145, 287, 535, 1277, 535, 1157, 535, 391, 161, 263,
+ /* 1180 */ 206, 381, 116, 116, 116, 116, 115, 115, 114, 114,
+ /* 1190 */ 114, 113, 415, 132, 132, 131, 131, 129, 129, 535,
+ /* 1200 */ 30, 535, 116, 116, 116, 116, 115, 115, 114, 114,
+ /* 1210 */ 114, 113, 415, 535, 216, 1062, 1276, 535, 370, 535,
+ /* 1220 */ 130, 130, 74, 74, 535, 915, 389, 876, 17, 437,
+ /* 1230 */ 429, 31, 875, 916, 76, 76, 266, 101, 73, 73,
+ /* 1240 */ 43, 43, 835, 834, 308, 47, 47, 95, 825, 943,
+ /* 1250 */ 441, 938, 241, 241, 305, 443, 313, 384, 241, 95,
+ /* 1260 */ 842, 843, 193, 465, 1209, 327, 237, 436, 95, 1011,
+ /* 1270 */ 1007, 909, 873, 237, 241, 107, 1023, 384, 1023, 955,
+ /* 1280 */ 1415, 119, 120, 110, 1136, 1136, 981, 984, 974, 974,
+ /* 1290 */ 117, 117, 118, 118, 118, 118, 1022, 809, 1022, 825,
+ /* 1300 */ 137, 119, 108, 110, 1136, 1136, 981, 984, 974, 974,
+ /* 1310 */ 117, 117, 118, 118, 118, 118, 874, 1414, 451, 107,
+ /* 1320 */ 1011, 314, 1273, 318, 218, 321, 323, 325, 1224, 1208,
+ /* 1330 */ 955, 330, 339, 340, 116, 116, 116, 116, 115, 115,
+ /* 1340 */ 114, 114, 114, 113, 415, 1285, 1322, 1260, 1493, 1470,
+ /* 1350 */ 1271, 283, 521, 1328, 116, 116, 116, 116, 115, 115,
+ /* 1360 */ 114, 114, 114, 113, 415, 1191, 1184, 1173, 1172, 1174,
+ /* 1370 */ 522, 1487, 211, 460, 384, 256, 199, 367, 1257, 342,
+ /* 1380 */ 195, 470, 307, 344, 11, 333, 525, 445, 1307, 1315,
+ /* 1390 */ 375, 203, 1207, 1151, 384, 346, 1387, 188, 360, 120,
+ /* 1400 */ 110, 1136, 1136, 981, 984, 974, 974, 117, 117, 118,
+ /* 1410 */ 118, 118, 118, 1386, 428, 1490, 245, 300, 348, 1148,
+ /* 1420 */ 110, 1136, 1136, 981, 984, 974, 974, 117, 117, 118,
+ /* 1430 */ 118, 118, 118, 189, 198, 1434, 1432, 78, 81, 163,
+ /* 1440 */ 82, 392, 439, 1392, 173, 105, 527, 35, 4, 157,
+ /* 1450 */ 1312, 116, 116, 116, 116, 115, 115, 114, 114, 114,
+ /* 1460 */ 113, 415, 530, 165, 93, 1304, 431, 432, 168, 463,
+ /* 1470 */ 221, 116, 116, 116, 116, 115, 115, 114, 114, 114,
+ /* 1480 */ 113, 415, 169, 452, 170, 416, 171, 374, 372, 438,
+ /* 1490 */ 36, 1318, 177, 225, 1381, 87, 458, 524, 1403, 316,
+ /* 1500 */ 257, 105, 527, 227, 4, 182, 461, 160, 320, 228,
+ /* 1510 */ 377, 1175, 476, 229, 1227, 1226, 405, 1225, 530, 1218,
+ /* 1520 */ 961, 378, 1199, 1198, 827, 332, 103, 103, 1197, 407,
+ /* 1530 */ 8, 1217, 1502, 104, 487, 416, 537, 536, 281, 282,
+ /* 1540 */ 951, 416, 490, 1268, 496, 92, 341, 243, 1269, 343,
+ /* 1550 */ 244, 1267, 122, 524, 345, 1461, 515, 288, 526, 10,
+ /* 1560 */ 354, 1266, 1460, 352, 504, 1250, 99, 1367, 94, 503,
+ /* 1570 */ 499, 951, 951, 953, 954, 27, 961, 347, 1249, 194,
+ /* 1580 */ 251, 358, 103, 103, 359, 1181, 34, 538, 1110, 104,
+ /* 1590 */ 255, 416, 537, 536, 286, 252, 951, 254, 539, 149,
+ /* 1600 */ 1170, 1419, 1165, 1420, 1418, 150, 1417, 135, 279, 785,
+ /* 1610 */ 151, 417, 1195, 196, 290, 210, 386, 1194, 269, 387,
+ /* 1620 */ 162, 1021, 133, 77, 1192, 1019, 935, 951, 951, 953,
+ /* 1630 */ 954, 27, 1479, 1104, 418, 164, 153, 268, 217, 166,
+ /* 1640 */ 859, 306, 366, 366, 365, 253, 363, 220, 1035, 798,
+ /* 1650 */ 172, 939, 105, 527, 155, 4, 394, 174, 396, 156,
+ /* 1660 */ 83, 1038, 213, 84, 294, 85, 86, 223, 222, 530,
+ /* 1670 */ 1034, 144, 293, 18, 224, 315, 241, 1027, 1145, 178,
+ /* 1680 */ 457, 226, 179, 37, 800, 334, 462, 230, 328, 466,
+ /* 1690 */ 180, 471, 416, 88, 19, 20, 89, 280, 838, 158,
+ /* 1700 */ 191, 90, 215, 478, 524, 1097, 204, 192, 987, 91,
+ /* 1710 */ 152, 1070, 39, 154, 1071, 504, 486, 40, 489, 205,
+ /* 1720 */ 505, 260, 105, 527, 214, 4, 908, 961, 262, 183,
+ /* 1730 */ 240, 21, 903, 103, 103, 107, 22, 1086, 23, 530,
+ /* 1740 */ 104, 1088, 416, 537, 536, 24, 1093, 951, 25, 1074,
+ /* 1750 */ 1090, 1094, 7, 33, 511, 186, 26, 1002, 385, 95,
+ /* 1760 */ 988, 986, 416, 288, 526, 990, 1044, 246, 1043, 247,
+ /* 1770 */ 991, 28, 41, 106, 524, 956, 810, 29, 951, 951,
+ /* 1780 */ 953, 954, 27, 531, 361, 504, 423, 248, 869, 249,
+ /* 1790 */ 503, 1495, 364, 1105, 1161, 1494, 1161, 961, 1161, 1161,
+ /* 1800 */ 1161, 1161, 1161, 103, 103, 1161, 1161, 1161, 1161, 1161,
+ /* 1810 */ 104, 1161, 416, 537, 536, 1104, 418, 951, 1161, 268,
+ /* 1820 */ 1161, 1161, 1161, 1161, 366, 366, 365, 253, 363, 1161,
+ /* 1830 */ 1161, 798, 1161, 1161, 1161, 1161, 105, 527, 1161, 4,
+ /* 1840 */ 1161, 1161, 1161, 1161, 213, 1161, 294, 1161, 951, 951,
+ /* 1850 */ 953, 954, 27, 530, 293, 1161, 1161, 1161, 1161, 1161,
+ /* 1860 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161,
+ /* 1870 */ 1161, 1161, 1161, 1161, 1161, 1161, 416, 1161, 1161, 1161,
+ /* 1880 */ 1161, 1161, 1161, 1161, 215, 1161, 1161, 1161, 524, 1161,
+ /* 1890 */ 1161, 1161, 152, 1161, 1161, 154, 105, 527, 1161, 4,
+ /* 1900 */ 1161, 1161, 1161, 1161, 1161, 1161, 214, 1161, 1161, 1161,
+ /* 1910 */ 1161, 961, 1161, 530, 1161, 1161, 1161, 103, 103, 880,
+ /* 1920 */ 1161, 1161, 1161, 1161, 104, 1161, 416, 537, 536, 1161,
+ /* 1930 */ 1161, 951, 1161, 1161, 1161, 1161, 416, 1161, 1161, 1161,
+ /* 1940 */ 385, 1161, 1161, 1161, 1161, 288, 526, 1161, 524, 1161,
+ /* 1950 */ 1161, 1161, 1161, 1161, 1161, 1161, 97, 527, 1161, 4,
+ /* 1960 */ 1161, 1161, 951, 951, 953, 954, 27, 1161, 423, 1161,
+ /* 1970 */ 1161, 961, 1161, 530, 1161, 1161, 1161, 103, 103, 1161,
+ /* 1980 */ 1161, 1161, 1161, 1161, 104, 1161, 416, 537, 536, 1161,
+ /* 1990 */ 1161, 951, 268, 1161, 1161, 1161, 416, 366, 366, 365,
+ /* 2000 */ 253, 363, 1161, 1161, 798, 1161, 1161, 1161, 524, 1161,
+ /* 2010 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 213, 1161, 294,
+ /* 2020 */ 1161, 1161, 951, 951, 953, 954, 27, 293, 1161, 1161,
+ /* 2030 */ 1161, 961, 1161, 1161, 1161, 1161, 1161, 103, 103, 1161,
+ /* 2040 */ 1161, 1161, 1161, 1161, 104, 1161, 416, 537, 536, 1161,
+ /* 2050 */ 1161, 951, 1161, 1161, 1161, 1161, 1161, 215, 1161, 1161,
+ /* 2060 */ 1161, 1161, 1161, 1161, 1161, 152, 1161, 1161, 154, 1161,
+ /* 2070 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 214,
+ /* 2080 */ 1161, 1161, 951, 951, 953, 954, 27, 1161, 1161, 1161,
+ /* 2090 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161,
+ /* 2100 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161,
+ /* 2110 */ 1161, 1161, 1161, 385, 1161, 1161, 1161, 1161, 288, 526,
+ /* 2120 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161,
+ /* 2130 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161,
+ /* 2140 */ 1161, 423,
};
static const YYCODETYPE yy_lookahead[] = {
- /* 0 */ 168, 163, 184, 238, 239, 240, 163, 163, 155, 156,
- /* 10 */ 157, 158, 159, 160, 163, 202, 203, 187, 165, 19,
- /* 20 */ 167, 163, 184, 185, 259, 202, 203, 174, 184, 185,
- /* 30 */ 174, 31, 238, 239, 240, 184, 185, 22, 23, 39,
- /* 40 */ 216, 26, 218, 43, 44, 45, 46, 47, 48, 49,
- /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 174, 206,
- /* 60 */ 207, 163, 206, 207, 220, 163, 163, 163, 238, 239,
- /* 70 */ 240, 59, 219, 229, 231, 219, 183, 245, 174, 223,
- /* 80 */ 224, 249, 184, 185, 191, 232, 184, 185, 184, 185,
- /* 90 */ 206, 207, 92, 93, 94, 95, 96, 97, 98, 99,
- /* 100 */ 100, 101, 102, 219, 102, 81, 91, 163, 96, 97,
- /* 110 */ 206, 207, 19, 275, 276, 262, 104, 105, 106, 107,
- /* 120 */ 163, 109, 220, 219, 220, 184, 275, 269, 277, 117,
- /* 130 */ 187, 229, 19, 229, 101, 102, 43, 44, 45, 46,
- /* 140 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
- /* 150 */ 57, 127, 128, 141, 184, 143, 43, 44, 45, 46,
- /* 160 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
- /* 170 */ 57, 268, 269, 275, 276, 197, 83, 233, 85, 163,
- /* 180 */ 67, 238, 239, 240, 134, 92, 93, 94, 95, 96,
- /* 190 */ 97, 98, 99, 100, 101, 102, 19, 54, 55, 56,
- /* 200 */ 57, 58, 152, 26, 247, 92, 93, 94, 95, 96,
- /* 210 */ 97, 98, 99, 100, 101, 102, 54, 55, 56, 57,
- /* 220 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
- /* 230 */ 53, 54, 55, 56, 57, 92, 93, 94, 95, 96,
- /* 240 */ 97, 98, 99, 100, 101, 102, 69, 96, 97, 98,
- /* 250 */ 99, 100, 101, 102, 92, 93, 94, 95, 96, 97,
- /* 260 */ 98, 99, 100, 101, 102, 73, 179, 180, 181, 92,
- /* 270 */ 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
- /* 280 */ 163, 191, 192, 163, 98, 99, 100, 101, 102, 19,
- /* 290 */ 200, 179, 180, 181, 24, 175, 92, 93, 94, 95,
- /* 300 */ 96, 97, 98, 99, 100, 101, 102, 163, 116, 117,
- /* 310 */ 118, 22, 163, 43, 44, 45, 46, 47, 48, 49,
- /* 320 */ 50, 51, 52, 53, 54, 55, 56, 57, 157, 158,
- /* 330 */ 159, 160, 163, 184, 185, 163, 165, 59, 167, 46,
- /* 340 */ 90, 76, 11, 174, 73, 174, 19, 198, 59, 19,
- /* 350 */ 72, 86, 81, 184, 185, 234, 106, 96, 97, 163,
- /* 360 */ 110, 182, 92, 93, 94, 95, 96, 97, 98, 99,
- /* 370 */ 100, 101, 102, 46, 230, 206, 207, 206, 207, 163,
- /* 380 */ 184, 185, 19, 105, 106, 107, 23, 116, 219, 220,
- /* 390 */ 219, 141, 142, 143, 105, 106, 107, 104, 127, 128,
- /* 400 */ 184, 185, 141, 232, 143, 59, 43, 44, 45, 46,
- /* 410 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
- /* 420 */ 57, 158, 108, 160, 59, 111, 112, 113, 165, 250,
- /* 430 */ 167, 104, 102, 262, 255, 121, 220, 174, 108, 109,
- /* 440 */ 110, 111, 112, 113, 114, 229, 182, 120, 117, 118,
- /* 450 */ 120, 105, 106, 107, 163, 92, 93, 94, 95, 96,
- /* 460 */ 97, 98, 99, 100, 101, 102, 163, 22, 59, 206,
- /* 470 */ 207, 106, 163, 26, 171, 19, 163, 193, 175, 23,
- /* 480 */ 163, 22, 219, 206, 207, 139, 163, 22, 59, 182,
- /* 490 */ 117, 118, 59, 184, 185, 232, 219, 184, 185, 43,
- /* 500 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
- /* 510 */ 54, 55, 56, 57, 105, 106, 107, 108, 59, 255,
- /* 520 */ 111, 112, 113, 90, 59, 262, 22, 98, 174, 12,
- /* 530 */ 121, 208, 163, 220, 105, 106, 107, 163, 105, 106,
- /* 540 */ 22, 96, 97, 110, 27, 238, 239, 240, 92, 93,
- /* 550 */ 94, 95, 96, 97, 98, 99, 100, 101, 102, 42,
- /* 560 */ 206, 207, 115, 59, 105, 106, 107, 163, 19, 59,
- /* 570 */ 163, 106, 23, 219, 141, 142, 143, 59, 163, 205,
- /* 580 */ 63, 59, 72, 22, 124, 59, 163, 270, 234, 129,
- /* 590 */ 73, 163, 43, 44, 45, 46, 47, 48, 49, 50,
- /* 600 */ 51, 52, 53, 54, 55, 56, 57, 184, 185, 105,
- /* 610 */ 106, 107, 184, 185, 163, 105, 106, 107, 265, 266,
- /* 620 */ 59, 198, 225, 105, 106, 107, 198, 105, 106, 107,
- /* 630 */ 163, 105, 106, 107, 163, 184, 185, 46, 47, 48,
- /* 640 */ 49, 92, 93, 94, 95, 96, 97, 98, 99, 100,
- /* 650 */ 101, 102, 163, 163, 132, 184, 185, 163, 132, 163,
- /* 660 */ 256, 19, 163, 163, 163, 23, 105, 106, 107, 198,
- /* 670 */ 163, 220, 205, 184, 185, 163, 35, 81, 184, 185,
- /* 680 */ 184, 185, 163, 184, 185, 43, 44, 45, 46, 47,
- /* 690 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
- /* 700 */ 163, 110, 163, 184, 185, 109, 205, 66, 163, 59,
- /* 710 */ 163, 21, 205, 16, 174, 74, 220, 198, 163, 220,
- /* 720 */ 230, 184, 185, 127, 128, 180, 181, 180, 181, 163,
- /* 730 */ 175, 242, 174, 233, 92, 93, 94, 95, 96, 97,
- /* 740 */ 98, 99, 100, 101, 102, 233, 206, 207, 26, 163,
- /* 750 */ 195, 207, 197, 26, 19, 105, 106, 107, 23, 219,
- /* 760 */ 119, 260, 26, 219, 206, 207, 174, 19, 174, 230,
- /* 770 */ 80, 174, 163, 174, 77, 163, 79, 219, 43, 44,
- /* 780 */ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
- /* 790 */ 55, 56, 57, 248, 12, 248, 184, 185, 206, 207,
- /* 800 */ 206, 207, 112, 206, 207, 206, 207, 206, 207, 27,
- /* 810 */ 163, 219, 123, 219, 125, 126, 219, 208, 219, 163,
- /* 820 */ 219, 22, 23, 23, 42, 26, 26, 92, 93, 94,
- /* 830 */ 95, 96, 97, 98, 99, 100, 101, 102, 163, 149,
- /* 840 */ 184, 185, 163, 107, 163, 63, 149, 19, 163, 127,
- /* 850 */ 128, 23, 22, 105, 24, 116, 117, 118, 131, 184,
- /* 860 */ 185, 163, 163, 184, 185, 184, 185, 19, 132, 184,
- /* 870 */ 185, 43, 44, 45, 46, 47, 48, 49, 50, 51,
- /* 880 */ 52, 53, 54, 55, 56, 57, 146, 163, 148, 59,
- /* 890 */ 91, 43, 44, 45, 46, 47, 48, 49, 50, 51,
- /* 900 */ 52, 53, 54, 55, 56, 57, 208, 107, 184, 185,
- /* 910 */ 7, 8, 9, 116, 117, 118, 163, 163, 163, 24,
- /* 920 */ 92, 93, 94, 95, 96, 97, 98, 99, 100, 101,
- /* 930 */ 102, 29, 132, 163, 163, 33, 106, 184, 185, 163,
- /* 940 */ 92, 93, 94, 95, 96, 97, 98, 99, 100, 101,
- /* 950 */ 102, 163, 163, 163, 59, 184, 185, 163, 22, 163,
- /* 960 */ 184, 185, 177, 178, 163, 163, 163, 65, 163, 199,
- /* 970 */ 163, 26, 184, 185, 184, 185, 163, 163, 184, 185,
- /* 980 */ 184, 185, 163, 98, 163, 184, 185, 184, 185, 184,
- /* 990 */ 185, 184, 185, 252, 205, 147, 163, 61, 184, 185,
- /* 1000 */ 163, 106, 163, 184, 185, 163, 163, 205, 163, 124,
- /* 1010 */ 163, 256, 163, 163, 129, 19, 163, 184, 185, 163,
- /* 1020 */ 199, 184, 185, 184, 185, 23, 184, 185, 26, 184,
- /* 1030 */ 185, 184, 185, 184, 185, 19, 163, 184, 185, 43,
- /* 1040 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
- /* 1050 */ 54, 55, 56, 57, 163, 163, 163, 184, 185, 43,
- /* 1060 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
- /* 1070 */ 54, 55, 56, 57, 16, 184, 185, 184, 185, 163,
- /* 1080 */ 163, 22, 23, 138, 163, 19, 163, 231, 92, 93,
- /* 1090 */ 94, 95, 96, 97, 98, 99, 100, 101, 102, 256,
- /* 1100 */ 163, 184, 185, 163, 163, 184, 185, 163, 92, 93,
- /* 1110 */ 94, 95, 96, 97, 98, 99, 100, 101, 102, 163,
- /* 1120 */ 59, 184, 185, 163, 184, 185, 177, 178, 184, 185,
- /* 1130 */ 163, 208, 163, 237, 163, 77, 163, 79, 163, 15,
- /* 1140 */ 184, 185, 237, 147, 184, 185, 24, 231, 153, 154,
- /* 1150 */ 91, 184, 185, 184, 185, 184, 185, 184, 185, 184,
- /* 1160 */ 185, 22, 23, 19, 163, 127, 128, 106, 24, 273,
- /* 1170 */ 271, 105, 231, 274, 263, 264, 223, 224, 273, 22,
- /* 1180 */ 118, 24, 23, 19, 60, 26, 163, 43, 44, 45,
- /* 1190 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
- /* 1200 */ 56, 57, 140, 23, 22, 163, 26, 43, 44, 45,
- /* 1210 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
- /* 1220 */ 56, 57, 23, 211, 23, 26, 31, 26, 23, 22,
- /* 1230 */ 91, 26, 231, 221, 39, 53, 92, 93, 94, 95,
- /* 1240 */ 96, 97, 98, 99, 100, 101, 102, 23, 23, 163,
- /* 1250 */ 26, 26, 130, 59, 109, 110, 92, 93, 94, 95,
- /* 1260 */ 96, 97, 98, 99, 100, 101, 102, 23, 7, 8,
- /* 1270 */ 26, 110, 23, 59, 23, 26, 19, 26, 141, 23,
- /* 1280 */ 143, 120, 26, 141, 163, 143, 23, 23, 163, 26,
- /* 1290 */ 26, 163, 163, 163, 163, 163, 19, 163, 163, 193,
- /* 1300 */ 106, 44, 45, 46, 47, 48, 49, 50, 51, 52,
- /* 1310 */ 53, 54, 55, 56, 57, 163, 193, 163, 163, 163,
- /* 1320 */ 106, 163, 45, 46, 47, 48, 49, 50, 51, 52,
- /* 1330 */ 53, 54, 55, 56, 57, 163, 163, 130, 222, 163,
- /* 1340 */ 163, 203, 163, 19, 20, 251, 22, 163, 163, 92,
- /* 1350 */ 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
- /* 1360 */ 36, 163, 209, 163, 261, 163, 163, 161, 222, 92,
- /* 1370 */ 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
- /* 1380 */ 210, 213, 222, 59, 222, 222, 182, 213, 213, 196,
- /* 1390 */ 257, 226, 226, 19, 20, 71, 22, 257, 188, 187,
- /* 1400 */ 192, 212, 187, 187, 226, 81, 210, 166, 60, 261,
- /* 1410 */ 36, 244, 130, 170, 90, 170, 38, 170, 139, 104,
- /* 1420 */ 96, 97, 48, 236, 22, 235, 43, 103, 201, 105,
- /* 1430 */ 106, 107, 138, 59, 110, 247, 213, 18, 204, 258,
- /* 1440 */ 204, 258, 204, 204, 170, 71, 18, 169, 213, 236,
- /* 1450 */ 213, 127, 128, 235, 201, 201, 82, 170, 169, 213,
- /* 1460 */ 146, 87, 62, 254, 90, 141, 142, 143, 144, 145,
- /* 1470 */ 96, 97, 253, 170, 169, 22, 170, 103, 169, 105,
- /* 1480 */ 106, 107, 189, 170, 110, 169, 189, 186, 19, 20,
- /* 1490 */ 104, 22, 194, 186, 186, 64, 115, 186, 194, 189,
- /* 1500 */ 188, 102, 133, 186, 186, 36, 186, 104, 189, 19,
- /* 1510 */ 20, 246, 22, 246, 189, 141, 142, 143, 144, 145,
- /* 1520 */ 0, 1, 2, 228, 228, 5, 36, 227, 59, 227,
- /* 1530 */ 10, 11, 12, 13, 14, 170, 84, 17, 134, 216,
- /* 1540 */ 71, 272, 270, 22, 137, 217, 22, 216, 227, 59,
- /* 1550 */ 30, 82, 32, 217, 228, 228, 87, 227, 170, 90,
- /* 1560 */ 40, 71, 146, 241, 215, 96, 97, 214, 136, 135,
- /* 1570 */ 213, 25, 103, 26, 105, 106, 107, 243, 173, 110,
- /* 1580 */ 90, 172, 13, 6, 164, 164, 96, 97, 98, 162,
- /* 1590 */ 70, 162, 162, 103, 176, 105, 106, 107, 78, 267,
- /* 1600 */ 110, 81, 267, 264, 182, 182, 182, 182, 88, 176,
- /* 1610 */ 141, 142, 143, 144, 145, 176, 190, 4, 182, 182,
- /* 1620 */ 182, 19, 20, 182, 22, 190, 3, 22, 151, 15,
- /* 1630 */ 89, 141, 142, 143, 144, 145, 16, 128, 36, 23,
- /* 1640 */ 23, 139, 122, 24, 119, 131, 20, 127, 128, 133,
- /* 1650 */ 16, 1, 140, 131, 119, 61, 139, 53, 37, 53,
- /* 1660 */ 53, 59, 53, 119, 105, 34, 130, 1, 5, 22,
- /* 1670 */ 150, 104, 149, 71, 26, 75, 68, 41, 68, 130,
- /* 1680 */ 104, 24, 20, 19, 82, 120, 114, 22, 28, 87,
- /* 1690 */ 22, 67, 90, 22, 67, 23, 22, 22, 96, 97,
- /* 1700 */ 67, 23, 138, 22, 37, 103, 153, 105, 106, 107,
- /* 1710 */ 1, 2, 110, 23, 5, 23, 23, 26, 22, 10,
- /* 1720 */ 11, 12, 13, 14, 24, 23, 17, 22, 24, 130,
- /* 1730 */ 23, 19, 20, 23, 22, 105, 22, 34, 85, 30,
- /* 1740 */ 34, 32, 26, 141, 142, 143, 144, 145, 36, 40,
- /* 1750 */ 132, 34, 75, 83, 23, 44, 24, 34, 23, 26,
- /* 1760 */ 26, 19, 20, 23, 22, 26, 23, 23, 23, 23,
- /* 1770 */ 22, 59, 11, 22, 22, 26, 23, 23, 36, 70,
- /* 1780 */ 22, 22, 124, 71, 130, 130, 130, 78, 23, 130,
- /* 1790 */ 81, 15, 1, 278, 278, 278, 278, 88, 278, 278,
- /* 1800 */ 278, 59, 90, 278, 278, 278, 278, 278, 96, 97,
- /* 1810 */ 278, 278, 278, 71, 278, 103, 278, 105, 106, 107,
- /* 1820 */ 278, 278, 110, 278, 278, 278, 278, 278, 278, 278,
- /* 1830 */ 278, 122, 90, 278, 278, 278, 127, 128, 96, 97,
- /* 1840 */ 278, 278, 278, 278, 278, 103, 278, 105, 106, 107,
- /* 1850 */ 278, 278, 110, 141, 142, 143, 144, 145, 278, 150,
- /* 1860 */ 278, 278, 278, 5, 278, 278, 278, 278, 10, 11,
- /* 1870 */ 12, 13, 14, 278, 278, 17, 278, 278, 278, 278,
- /* 1880 */ 278, 278, 278, 141, 142, 143, 144, 145, 30, 278,
- /* 1890 */ 32, 278, 278, 278, 278, 278, 278, 278, 40, 278,
- /* 1900 */ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,
- /* 1910 */ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,
- /* 1920 */ 278, 278, 278, 278, 278, 278, 278, 278, 70, 278,
- /* 1930 */ 278, 278, 278, 278, 278, 278, 78, 278, 278, 81,
- /* 1940 */ 278, 278, 278, 278, 278, 278, 88, 278, 278, 278,
- /* 1950 */ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,
- /* 1960 */ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,
- /* 1970 */ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,
- /* 1980 */ 122, 278, 278, 278, 278, 127, 128, 278, 278, 278,
- /* 1990 */ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,
- /* 2000 */ 278, 278, 278, 278, 278, 278, 278, 278, 150, 278,
- /* 2010 */ 278, 278, 278, 278, 278, 278, 278, 278, 278,
+ /* 0 */ 184, 184, 259, 260, 261, 259, 260, 261, 176, 177,
+ /* 10 */ 178, 179, 180, 181, 184, 208, 212, 213, 186, 19,
+ /* 20 */ 188, 205, 206, 280, 205, 221, 22, 195, 24, 195,
+ /* 30 */ 208, 31, 195, 205, 29, 205, 206, 255, 33, 39,
+ /* 40 */ 200, 201, 202, 43, 44, 45, 46, 47, 48, 49,
+ /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 205, 227,
+ /* 60 */ 228, 227, 228, 59, 227, 228, 259, 260, 261, 252,
+ /* 70 */ 65, 241, 240, 184, 240, 223, 224, 240, 244, 245,
+ /* 80 */ 250, 259, 260, 261, 19, 253, 54, 55, 56, 57,
+ /* 90 */ 58, 184, 255, 184, 205, 206, 96, 97, 98, 99,
+ /* 100 */ 100, 101, 102, 103, 104, 105, 106, 46, 47, 48,
+ /* 110 */ 49, 46, 296, 297, 110, 283, 19, 96, 97, 98,
+ /* 120 */ 99, 100, 101, 102, 103, 104, 105, 106, 96, 97,
+ /* 130 */ 98, 99, 100, 101, 102, 103, 104, 105, 106, 94,
+ /* 140 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ /* 150 */ 53, 54, 55, 56, 57, 110, 106, 73, 251, 114,
+ /* 160 */ 73, 178, 179, 180, 181, 59, 184, 292, 81, 186,
+ /* 170 */ 295, 188, 218, 108, 19, 114, 184, 11, 195, 184,
+ /* 180 */ 83, 184, 85, 54, 55, 56, 57, 205, 206, 124,
+ /* 190 */ 145, 146, 147, 96, 97, 98, 99, 100, 101, 102,
+ /* 200 */ 103, 104, 105, 106, 120, 121, 122, 120, 102, 81,
+ /* 210 */ 227, 228, 220, 19, 16, 109, 110, 111, 131, 132,
+ /* 220 */ 26, 184, 184, 240, 229, 96, 97, 98, 99, 100,
+ /* 230 */ 101, 102, 103, 104, 105, 106, 253, 43, 44, 45,
+ /* 240 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
+ /* 250 */ 56, 57, 100, 101, 102, 103, 104, 105, 106, 131,
+ /* 260 */ 132, 106, 127, 69, 129, 130, 283, 112, 113, 114,
+ /* 270 */ 115, 116, 117, 118, 81, 77, 76, 79, 296, 124,
+ /* 280 */ 298, 203, 184, 19, 84, 59, 86, 121, 122, 89,
+ /* 290 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105,
+ /* 300 */ 106, 184, 35, 205, 206, 22, 113, 43, 44, 45,
+ /* 310 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
+ /* 320 */ 56, 57, 205, 206, 131, 132, 100, 101, 291, 292,
+ /* 330 */ 114, 67, 295, 66, 108, 109, 110, 111, 138, 113,
+ /* 340 */ 124, 74, 59, 179, 184, 181, 184, 121, 22, 271,
+ /* 350 */ 186, 19, 188, 184, 276, 59, 24, 184, 241, 195,
+ /* 360 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105,
+ /* 370 */ 106, 145, 59, 147, 184, 43, 44, 45, 46, 47,
+ /* 380 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ /* 390 */ 123, 227, 228, 110, 296, 297, 22, 23, 184, 102,
+ /* 400 */ 103, 104, 105, 106, 240, 109, 110, 111, 112, 195,
+ /* 410 */ 204, 115, 116, 117, 22, 184, 226, 253, 212, 205,
+ /* 420 */ 206, 125, 109, 110, 111, 22, 100, 101, 96, 97,
+ /* 430 */ 98, 99, 100, 101, 102, 103, 104, 105, 106, 184,
+ /* 440 */ 59, 227, 228, 121, 122, 59, 277, 283, 19, 289,
+ /* 450 */ 290, 59, 23, 76, 240, 241, 143, 76, 72, 189,
+ /* 460 */ 205, 206, 59, 86, 250, 84, 89, 86, 203, 95,
+ /* 470 */ 89, 281, 43, 44, 45, 46, 47, 48, 49, 50,
+ /* 480 */ 51, 52, 53, 54, 55, 56, 57, 227, 228, 184,
+ /* 490 */ 109, 110, 111, 12, 184, 109, 110, 111, 184, 184,
+ /* 500 */ 240, 109, 110, 111, 184, 195, 214, 59, 27, 184,
+ /* 510 */ 205, 206, 109, 110, 111, 205, 206, 184, 263, 138,
+ /* 520 */ 205, 206, 184, 42, 22, 96, 97, 98, 99, 100,
+ /* 530 */ 101, 102, 103, 104, 105, 106, 266, 227, 228, 59,
+ /* 540 */ 270, 276, 94, 66, 63, 19, 241, 22, 26, 23,
+ /* 550 */ 240, 241, 72, 59, 73, 250, 241, 109, 110, 82,
+ /* 560 */ 22, 59, 114, 223, 224, 250, 252, 59, 91, 43,
+ /* 570 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
+ /* 580 */ 54, 55, 56, 57, 59, 184, 26, 59, 268, 109,
+ /* 590 */ 110, 111, 184, 145, 146, 147, 112, 59, 203, 115,
+ /* 600 */ 116, 117, 277, 109, 110, 111, 205, 206, 195, 125,
+ /* 610 */ 277, 109, 110, 111, 100, 101, 139, 109, 110, 111,
+ /* 620 */ 219, 184, 96, 97, 98, 99, 100, 101, 102, 103,
+ /* 630 */ 104, 105, 106, 111, 109, 110, 111, 109, 110, 111,
+ /* 640 */ 227, 228, 19, 184, 136, 184, 23, 109, 110, 111,
+ /* 650 */ 200, 201, 202, 240, 259, 260, 261, 195, 136, 145,
+ /* 660 */ 184, 147, 184, 184, 136, 214, 43, 44, 45, 46,
+ /* 670 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ /* 680 */ 57, 205, 206, 205, 206, 227, 228, 184, 229, 227,
+ /* 690 */ 228, 131, 132, 184, 59, 219, 184, 219, 240, 291,
+ /* 700 */ 292, 184, 240, 295, 105, 106, 22, 23, 205, 206,
+ /* 710 */ 26, 184, 251, 184, 205, 206, 184, 205, 206, 96,
+ /* 720 */ 97, 98, 99, 100, 101, 102, 103, 104, 105, 106,
+ /* 730 */ 251, 219, 205, 206, 205, 206, 184, 205, 206, 19,
+ /* 740 */ 184, 16, 184, 23, 241, 110, 219, 21, 219, 184,
+ /* 750 */ 241, 219, 286, 287, 195, 184, 195, 205, 206, 201,
+ /* 760 */ 202, 205, 206, 43, 44, 45, 46, 47, 48, 49,
+ /* 770 */ 50, 51, 52, 53, 54, 55, 56, 57, 184, 95,
+ /* 780 */ 22, 23, 184, 26, 26, 220, 227, 228, 227, 228,
+ /* 790 */ 196, 184, 23, 241, 26, 26, 195, 241, 184, 240,
+ /* 800 */ 12, 240, 77, 26, 79, 195, 80, 290, 201, 202,
+ /* 810 */ 216, 184, 218, 195, 184, 27, 96, 97, 98, 99,
+ /* 820 */ 100, 101, 102, 103, 104, 105, 106, 269, 227, 228,
+ /* 830 */ 42, 184, 205, 206, 184, 184, 19, 227, 228, 192,
+ /* 840 */ 23, 240, 116, 196, 76, 227, 228, 120, 121, 122,
+ /* 850 */ 240, 63, 254, 95, 86, 205, 206, 89, 240, 184,
+ /* 860 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ /* 870 */ 53, 54, 55, 56, 57, 184, 269, 184, 153, 153,
+ /* 880 */ 111, 184, 7, 8, 9, 184, 138, 184, 184, 196,
+ /* 890 */ 184, 120, 121, 122, 184, 138, 205, 206, 184, 102,
+ /* 900 */ 184, 184, 205, 206, 156, 136, 205, 206, 205, 206,
+ /* 910 */ 198, 199, 135, 96, 97, 98, 99, 100, 101, 102,
+ /* 920 */ 103, 104, 105, 106, 184, 128, 184, 184, 184, 254,
+ /* 930 */ 133, 184, 237, 19, 239, 229, 226, 23, 292, 184,
+ /* 940 */ 226, 295, 226, 226, 184, 205, 206, 205, 206, 205,
+ /* 950 */ 206, 184, 292, 19, 184, 295, 252, 43, 44, 45,
+ /* 960 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
+ /* 970 */ 56, 57, 205, 206, 184, 205, 206, 43, 44, 45,
+ /* 980 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
+ /* 990 */ 56, 57, 157, 158, 26, 205, 206, 254, 26, 252,
+ /* 1000 */ 184, 15, 184, 184, 184, 292, 184, 252, 295, 24,
+ /* 1010 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105,
+ /* 1020 */ 106, 205, 206, 205, 206, 205, 206, 205, 206, 184,
+ /* 1030 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105,
+ /* 1040 */ 106, 184, 184, 184, 59, 184, 60, 184, 229, 184,
+ /* 1050 */ 205, 206, 184, 258, 184, 19, 184, 19, 184, 246,
+ /* 1060 */ 184, 258, 205, 206, 205, 206, 205, 206, 205, 206,
+ /* 1070 */ 205, 206, 184, 205, 206, 205, 206, 205, 206, 205,
+ /* 1080 */ 206, 205, 206, 292, 226, 151, 295, 184, 228, 294,
+ /* 1090 */ 184, 119, 184, 205, 206, 110, 150, 294, 152, 184,
+ /* 1100 */ 240, 184, 22, 23, 23, 19, 184, 26, 205, 206,
+ /* 1110 */ 142, 205, 206, 205, 206, 184, 198, 199, 131, 132,
+ /* 1120 */ 205, 206, 205, 206, 22, 19, 24, 205, 206, 43,
+ /* 1130 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
+ /* 1140 */ 54, 55, 56, 57, 184, 109, 184, 109, 184, 43,
+ /* 1150 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
+ /* 1160 */ 54, 55, 56, 57, 46, 205, 206, 205, 206, 205,
+ /* 1170 */ 206, 232, 184, 184, 184, 95, 184, 284, 285, 244,
+ /* 1180 */ 245, 242, 96, 97, 98, 99, 100, 101, 102, 103,
+ /* 1190 */ 104, 105, 106, 205, 206, 205, 206, 205, 206, 184,
+ /* 1200 */ 22, 184, 96, 97, 98, 99, 100, 101, 102, 103,
+ /* 1210 */ 104, 105, 106, 184, 24, 23, 184, 184, 26, 184,
+ /* 1220 */ 205, 206, 205, 206, 184, 31, 108, 128, 22, 122,
+ /* 1230 */ 184, 53, 133, 39, 205, 206, 22, 151, 205, 206,
+ /* 1240 */ 205, 206, 113, 114, 23, 205, 206, 26, 59, 23,
+ /* 1250 */ 23, 144, 26, 26, 184, 23, 23, 19, 26, 26,
+ /* 1260 */ 7, 8, 24, 23, 214, 23, 26, 61, 26, 59,
+ /* 1270 */ 23, 23, 23, 26, 26, 26, 145, 19, 147, 59,
+ /* 1280 */ 184, 43, 44, 45, 46, 47, 48, 49, 50, 51,
+ /* 1290 */ 52, 53, 54, 55, 56, 57, 145, 23, 147, 110,
+ /* 1300 */ 26, 43, 44, 45, 46, 47, 48, 49, 50, 51,
+ /* 1310 */ 52, 53, 54, 55, 56, 57, 23, 184, 184, 26,
+ /* 1320 */ 110, 184, 184, 184, 134, 184, 184, 184, 184, 184,
+ /* 1330 */ 110, 184, 184, 184, 96, 97, 98, 99, 100, 101,
+ /* 1340 */ 102, 103, 104, 105, 106, 184, 184, 184, 134, 300,
+ /* 1350 */ 184, 243, 184, 184, 96, 97, 98, 99, 100, 101,
+ /* 1360 */ 102, 103, 104, 105, 106, 184, 184, 184, 184, 184,
+ /* 1370 */ 224, 184, 282, 273, 19, 272, 203, 182, 243, 243,
+ /* 1380 */ 230, 209, 278, 243, 231, 208, 265, 278, 234, 234,
+ /* 1390 */ 234, 217, 213, 60, 19, 243, 208, 237, 233, 44,
+ /* 1400 */ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
+ /* 1410 */ 55, 56, 57, 208, 247, 187, 134, 247, 247, 38,
+ /* 1420 */ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
+ /* 1430 */ 55, 56, 57, 237, 231, 191, 191, 279, 279, 282,
+ /* 1440 */ 143, 191, 108, 268, 22, 19, 20, 256, 22, 43,
+ /* 1450 */ 257, 96, 97, 98, 99, 100, 101, 102, 103, 104,
+ /* 1460 */ 105, 106, 36, 222, 142, 234, 18, 191, 225, 18,
+ /* 1470 */ 190, 96, 97, 98, 99, 100, 101, 102, 103, 104,
+ /* 1480 */ 105, 106, 225, 191, 225, 59, 225, 257, 234, 234,
+ /* 1490 */ 256, 222, 222, 190, 234, 150, 62, 71, 275, 274,
+ /* 1500 */ 191, 19, 20, 190, 22, 22, 210, 81, 191, 190,
+ /* 1510 */ 210, 191, 108, 190, 207, 207, 64, 207, 36, 215,
+ /* 1520 */ 94, 210, 207, 209, 119, 207, 100, 101, 207, 106,
+ /* 1530 */ 48, 215, 207, 107, 210, 109, 110, 111, 267, 267,
+ /* 1540 */ 114, 59, 210, 249, 137, 108, 248, 191, 249, 248,
+ /* 1550 */ 88, 249, 141, 71, 248, 299, 138, 131, 132, 22,
+ /* 1560 */ 191, 249, 299, 237, 82, 238, 150, 262, 140, 87,
+ /* 1570 */ 139, 145, 146, 147, 148, 149, 94, 248, 238, 236,
+ /* 1580 */ 25, 235, 100, 101, 234, 194, 26, 193, 13, 107,
+ /* 1590 */ 6, 109, 110, 111, 264, 185, 114, 185, 183, 197,
+ /* 1600 */ 183, 203, 183, 203, 203, 197, 203, 211, 211, 4,
+ /* 1610 */ 197, 3, 203, 22, 155, 15, 288, 203, 93, 288,
+ /* 1620 */ 285, 23, 16, 203, 203, 23, 132, 145, 146, 147,
+ /* 1630 */ 148, 149, 0, 1, 2, 143, 123, 5, 24, 135,
+ /* 1640 */ 20, 16, 10, 11, 12, 13, 14, 137, 1, 17,
+ /* 1650 */ 135, 144, 19, 20, 123, 22, 61, 143, 37, 123,
+ /* 1660 */ 53, 109, 30, 53, 32, 53, 53, 134, 34, 36,
+ /* 1670 */ 1, 5, 40, 22, 108, 153, 26, 68, 75, 68,
+ /* 1680 */ 41, 134, 108, 24, 20, 124, 19, 118, 23, 67,
+ /* 1690 */ 22, 67, 59, 22, 22, 22, 22, 67, 28, 37,
+ /* 1700 */ 23, 142, 70, 22, 71, 23, 157, 23, 23, 26,
+ /* 1710 */ 78, 23, 22, 81, 23, 82, 24, 22, 24, 134,
+ /* 1720 */ 87, 23, 19, 20, 92, 22, 109, 94, 23, 22,
+ /* 1730 */ 34, 34, 136, 100, 101, 26, 34, 85, 34, 36,
+ /* 1740 */ 107, 83, 109, 110, 111, 34, 90, 114, 34, 23,
+ /* 1750 */ 75, 75, 44, 22, 24, 26, 34, 23, 126, 26,
+ /* 1760 */ 23, 23, 59, 131, 132, 23, 23, 26, 23, 22,
+ /* 1770 */ 11, 22, 22, 22, 71, 23, 23, 22, 145, 146,
+ /* 1780 */ 147, 148, 149, 26, 23, 82, 154, 134, 128, 134,
+ /* 1790 */ 87, 134, 15, 1, 301, 134, 301, 94, 301, 301,
+ /* 1800 */ 301, 301, 301, 100, 101, 301, 301, 301, 301, 301,
+ /* 1810 */ 107, 301, 109, 110, 111, 1, 2, 114, 301, 5,
+ /* 1820 */ 301, 301, 301, 301, 10, 11, 12, 13, 14, 301,
+ /* 1830 */ 301, 17, 301, 301, 301, 301, 19, 20, 301, 22,
+ /* 1840 */ 301, 301, 301, 301, 30, 301, 32, 301, 145, 146,
+ /* 1850 */ 147, 148, 149, 36, 40, 301, 301, 301, 301, 301,
+ /* 1860 */ 301, 301, 301, 301, 301, 301, 301, 301, 301, 301,
+ /* 1870 */ 301, 301, 301, 301, 301, 301, 59, 301, 301, 301,
+ /* 1880 */ 301, 301, 301, 301, 70, 301, 301, 301, 71, 301,
+ /* 1890 */ 301, 301, 78, 301, 301, 81, 19, 20, 301, 22,
+ /* 1900 */ 301, 301, 301, 301, 301, 301, 92, 301, 301, 301,
+ /* 1910 */ 301, 94, 301, 36, 301, 301, 301, 100, 101, 102,
+ /* 1920 */ 301, 301, 301, 301, 107, 301, 109, 110, 111, 301,
+ /* 1930 */ 301, 114, 301, 301, 301, 301, 59, 301, 301, 301,
+ /* 1940 */ 126, 301, 301, 301, 301, 131, 132, 301, 71, 301,
+ /* 1950 */ 301, 301, 301, 301, 301, 301, 19, 20, 301, 22,
+ /* 1960 */ 301, 301, 145, 146, 147, 148, 149, 301, 154, 301,
+ /* 1970 */ 301, 94, 301, 36, 301, 301, 301, 100, 101, 301,
+ /* 1980 */ 301, 301, 301, 301, 107, 301, 109, 110, 111, 301,
+ /* 1990 */ 301, 114, 5, 301, 301, 301, 59, 10, 11, 12,
+ /* 2000 */ 13, 14, 301, 301, 17, 301, 301, 301, 71, 301,
+ /* 2010 */ 301, 301, 301, 301, 301, 301, 301, 30, 301, 32,
+ /* 2020 */ 301, 301, 145, 146, 147, 148, 149, 40, 301, 301,
+ /* 2030 */ 301, 94, 301, 301, 301, 301, 301, 100, 101, 301,
+ /* 2040 */ 301, 301, 301, 301, 107, 301, 109, 110, 111, 301,
+ /* 2050 */ 301, 114, 301, 301, 301, 301, 301, 70, 301, 301,
+ /* 2060 */ 301, 301, 301, 301, 301, 78, 301, 301, 81, 301,
+ /* 2070 */ 301, 301, 301, 301, 301, 301, 301, 301, 301, 92,
+ /* 2080 */ 301, 301, 145, 146, 147, 148, 149, 301, 301, 301,
+ /* 2090 */ 301, 301, 301, 301, 301, 301, 301, 301, 301, 301,
+ /* 2100 */ 301, 301, 301, 301, 301, 301, 301, 301, 301, 301,
+ /* 2110 */ 301, 301, 301, 126, 301, 301, 301, 301, 131, 132,
+ /* 2120 */ 301, 301, 301, 301, 301, 301, 301, 301, 301, 301,
+ /* 2130 */ 301, 301, 301, 301, 301, 301, 301, 301, 301, 301,
+ /* 2140 */ 301, 154, 301, 301, 301, 301, 301, 301, 301, 301,
+ /* 2150 */ 301, 301, 301, 301, 301, 301, 301, 301, 301, 301,
+ /* 2160 */ 301, 301, 301, 301, 301, 301, 301, 301, 301,
};
-#define YY_SHIFT_COUNT (523)
+#define YY_SHIFT_COUNT (540)
#define YY_SHIFT_MIN (0)
-#define YY_SHIFT_MAX (1858)
+#define YY_SHIFT_MAX (1987)
static const unsigned short int yy_shift_ofst[] = {
- /* 0 */ 1709, 1520, 1858, 1324, 1324, 24, 1374, 1469, 1602, 1712,
- /* 10 */ 1712, 1712, 271, 0, 0, 113, 1016, 1712, 1712, 1712,
- /* 20 */ 1712, 1712, 1712, 1712, 1712, 1712, 1712, 12, 12, 409,
- /* 30 */ 596, 24, 24, 24, 24, 24, 24, 93, 177, 270,
- /* 40 */ 363, 456, 549, 642, 735, 828, 848, 996, 1144, 1016,
- /* 50 */ 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016,
- /* 60 */ 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1164, 1016, 1257,
- /* 70 */ 1277, 1277, 1490, 1712, 1712, 1712, 1712, 1712, 1712, 1712,
- /* 80 */ 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712,
- /* 90 */ 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712,
- /* 100 */ 1712, 1712, 1712, 1712, 1712, 1742, 1712, 1712, 1712, 1712,
- /* 110 */ 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 143,
- /* 120 */ 162, 162, 162, 162, 162, 204, 151, 186, 650, 690,
- /* 130 */ 327, 650, 261, 261, 650, 722, 722, 722, 722, 373,
- /* 140 */ 33, 2, 2009, 2009, 330, 330, 330, 346, 289, 278,
- /* 150 */ 289, 289, 517, 517, 459, 510, 15, 799, 650, 650,
- /* 160 */ 650, 650, 650, 650, 650, 650, 650, 650, 650, 650,
- /* 170 */ 650, 650, 650, 650, 650, 650, 650, 650, 650, 650,
- /* 180 */ 331, 365, 995, 995, 265, 365, 50, 1038, 2009, 2009,
- /* 190 */ 2009, 433, 250, 250, 504, 314, 429, 518, 522, 526,
- /* 200 */ 561, 650, 650, 650, 650, 650, 650, 650, 650, 650,
- /* 210 */ 192, 650, 650, 650, 650, 650, 650, 650, 650, 650,
- /* 220 */ 650, 650, 650, 641, 641, 641, 650, 650, 650, 650,
- /* 230 */ 800, 650, 650, 650, 830, 650, 650, 782, 650, 650,
- /* 240 */ 650, 650, 650, 650, 650, 650, 739, 902, 689, 895,
- /* 250 */ 895, 895, 895, 736, 689, 689, 885, 445, 903, 1124,
- /* 260 */ 945, 748, 748, 1066, 945, 945, 1066, 447, 1002, 293,
- /* 270 */ 1195, 1195, 1195, 748, 740, 727, 460, 1157, 1348, 1282,
- /* 280 */ 1282, 1378, 1378, 1282, 1279, 1315, 1402, 1383, 1294, 1419,
- /* 290 */ 1419, 1419, 1419, 1282, 1428, 1294, 1294, 1315, 1402, 1383,
- /* 300 */ 1383, 1294, 1282, 1428, 1314, 1400, 1282, 1428, 1453, 1282,
- /* 310 */ 1428, 1282, 1428, 1453, 1386, 1386, 1386, 1431, 1453, 1386,
- /* 320 */ 1381, 1386, 1431, 1386, 1386, 1453, 1399, 1399, 1453, 1369,
- /* 330 */ 1403, 1369, 1403, 1369, 1403, 1369, 1403, 1282, 1404, 1452,
- /* 340 */ 1521, 1407, 1404, 1524, 1282, 1416, 1407, 1432, 1434, 1294,
- /* 350 */ 1546, 1547, 1569, 1569, 1577, 1577, 1577, 2009, 2009, 2009,
- /* 360 */ 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009,
- /* 370 */ 2009, 2009, 2009, 591, 697, 1059, 1139, 1058, 797, 465,
- /* 380 */ 1159, 1182, 1122, 1062, 1180, 936, 1199, 1201, 1205, 1224,
- /* 390 */ 1225, 1244, 1061, 1145, 1261, 1161, 1194, 1249, 1251, 1256,
- /* 400 */ 1137, 1142, 1263, 1264, 1214, 1207, 1613, 1623, 1605, 1477,
- /* 410 */ 1614, 1541, 1620, 1616, 1617, 1509, 1502, 1525, 1619, 1514,
- /* 420 */ 1626, 1516, 1634, 1650, 1522, 1512, 1535, 1594, 1621, 1517,
- /* 430 */ 1604, 1606, 1607, 1609, 1544, 1559, 1631, 1536, 1666, 1663,
- /* 440 */ 1647, 1567, 1523, 1608, 1648, 1610, 1600, 1636, 1549, 1576,
- /* 450 */ 1657, 1662, 1664, 1565, 1572, 1665, 1624, 1668, 1671, 1672,
- /* 460 */ 1674, 1627, 1660, 1675, 1633, 1667, 1678, 1564, 1681, 1553,
- /* 470 */ 1690, 1692, 1691, 1693, 1696, 1700, 1702, 1705, 1704, 1599,
- /* 480 */ 1707, 1710, 1630, 1703, 1714, 1618, 1716, 1706, 1716, 1717,
- /* 490 */ 1653, 1677, 1670, 1711, 1731, 1732, 1733, 1734, 1723, 1735,
- /* 500 */ 1716, 1740, 1743, 1744, 1745, 1739, 1746, 1748, 1761, 1751,
- /* 510 */ 1752, 1753, 1754, 1758, 1759, 1749, 1658, 1654, 1655, 1656,
- /* 520 */ 1659, 1765, 1776, 1791,
+ /* 0 */ 1814, 1632, 1987, 1426, 1426, 128, 1482, 1633, 1703, 1877,
+ /* 10 */ 1877, 1877, 87, 0, 0, 264, 1106, 1877, 1877, 1877,
+ /* 20 */ 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877,
+ /* 30 */ 226, 226, 381, 381, 296, 193, 128, 128, 128, 128,
+ /* 40 */ 128, 128, 97, 194, 332, 429, 526, 623, 720, 817,
+ /* 50 */ 914, 934, 1086, 1238, 1106, 1106, 1106, 1106, 1106, 1106,
+ /* 60 */ 1106, 1106, 1106, 1106, 1106, 1106, 1106, 1106, 1106, 1106,
+ /* 70 */ 1106, 1106, 1258, 1106, 1355, 1375, 1375, 1817, 1877, 1877,
+ /* 80 */ 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877,
+ /* 90 */ 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877,
+ /* 100 */ 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877,
+ /* 110 */ 1937, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877,
+ /* 120 */ 1877, 1877, 1877, 1877, 32, 129, 129, 129, 129, 129,
+ /* 130 */ 21, 152, 297, 494, 726, 65, 494, 514, 514, 494,
+ /* 140 */ 560, 560, 560, 560, 322, 599, 50, 2142, 2142, 155,
+ /* 150 */ 155, 155, 313, 392, 386, 392, 392, 481, 481, 200,
+ /* 160 */ 480, 684, 758, 494, 494, 494, 494, 494, 494, 494,
+ /* 170 */ 494, 494, 494, 494, 494, 494, 494, 494, 494, 494,
+ /* 180 */ 494, 494, 494, 494, 768, 768, 494, 166, 377, 377,
+ /* 190 */ 635, 835, 835, 635, 748, 987, 2142, 2142, 2142, 448,
+ /* 200 */ 45, 45, 403, 484, 502, 106, 525, 508, 528, 538,
+ /* 210 */ 494, 494, 494, 494, 494, 494, 494, 494, 494, 84,
+ /* 220 */ 494, 494, 494, 494, 494, 494, 494, 494, 494, 494,
+ /* 230 */ 494, 494, 267, 267, 267, 494, 494, 494, 494, 769,
+ /* 240 */ 494, 494, 494, 4, 477, 494, 494, 788, 494, 494,
+ /* 250 */ 494, 494, 494, 494, 494, 494, 727, 5, 135, 985,
+ /* 260 */ 985, 985, 985, 522, 135, 135, 797, 326, 875, 986,
+ /* 270 */ 968, 1036, 1036, 1038, 968, 968, 1038, 972, 1081, 1118,
+ /* 280 */ 1194, 1194, 1194, 1036, 757, 757, 946, 777, 1099, 1102,
+ /* 290 */ 1333, 1282, 1282, 1381, 1381, 1282, 1297, 1334, 1422, 1406,
+ /* 300 */ 1322, 1448, 1448, 1448, 1448, 1282, 1451, 1322, 1322, 1334,
+ /* 310 */ 1422, 1406, 1406, 1322, 1282, 1451, 1345, 1434, 1282, 1451,
+ /* 320 */ 1483, 1282, 1451, 1282, 1451, 1483, 1404, 1404, 1404, 1452,
+ /* 330 */ 1483, 1404, 1405, 1404, 1452, 1404, 1404, 1483, 1423, 1423,
+ /* 340 */ 1483, 1407, 1437, 1407, 1437, 1407, 1437, 1407, 1437, 1282,
+ /* 350 */ 1462, 1462, 1411, 1418, 1537, 1282, 1416, 1411, 1428, 1431,
+ /* 360 */ 1322, 1555, 1560, 1575, 1575, 1584, 1584, 1584, 2142, 2142,
+ /* 370 */ 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142,
+ /* 380 */ 2142, 2142, 2142, 2142, 61, 725, 374, 1080, 198, 771,
+ /* 390 */ 283, 1192, 1178, 1190, 1107, 1221, 1206, 1226, 1227, 1232,
+ /* 400 */ 1233, 1240, 1242, 1189, 1129, 1253, 216, 1210, 1247, 1248,
+ /* 410 */ 1249, 1131, 1151, 1274, 1293, 1220, 1214, 1605, 1608, 1591,
+ /* 420 */ 1459, 1600, 1525, 1606, 1598, 1602, 1494, 1492, 1513, 1614,
+ /* 430 */ 1504, 1620, 1510, 1625, 1647, 1515, 1507, 1531, 1595, 1621,
+ /* 440 */ 1514, 1607, 1610, 1612, 1613, 1536, 1552, 1634, 1533, 1669,
+ /* 450 */ 1666, 1651, 1566, 1522, 1609, 1650, 1611, 1603, 1639, 1547,
+ /* 460 */ 1574, 1659, 1664, 1667, 1561, 1569, 1668, 1622, 1671, 1672,
+ /* 470 */ 1665, 1673, 1624, 1670, 1674, 1630, 1662, 1677, 1559, 1681,
+ /* 480 */ 1682, 1549, 1684, 1685, 1683, 1688, 1690, 1692, 1691, 1695,
+ /* 490 */ 1694, 1585, 1698, 1705, 1617, 1696, 1707, 1596, 1709, 1697,
+ /* 500 */ 1702, 1704, 1711, 1652, 1675, 1658, 1708, 1676, 1656, 1714,
+ /* 510 */ 1726, 1731, 1730, 1729, 1733, 1722, 1734, 1709, 1737, 1738,
+ /* 520 */ 1742, 1743, 1741, 1745, 1747, 1759, 1749, 1750, 1752, 1753,
+ /* 530 */ 1751, 1755, 1757, 1660, 1653, 1655, 1657, 1661, 1761, 1777,
+ /* 540 */ 1792,
};
-#define YY_REDUCE_COUNT (372)
-#define YY_REDUCE_MIN (-235)
-#define YY_REDUCE_MAX (1441)
+#define YY_REDUCE_COUNT (383)
+#define YY_REDUCE_MIN (-257)
+#define YY_REDUCE_MAX (1421)
static const short yy_reduce_ofst[] = {
- /* 0 */ -147, 171, 263, -96, 169, -144, -162, -149, -102, -156,
- /* 10 */ -98, 216, 354, -170, -57, -235, 307, 149, 423, 428,
- /* 20 */ 471, 313, 451, 519, 489, 496, 499, 545, 547, 555,
- /* 30 */ -116, 540, 558, 592, 594, 597, 599, -206, -206, -206,
- /* 40 */ -206, -206, -206, -206, -206, -206, -206, -206, -206, -206,
- /* 50 */ -206, -206, -206, -206, -206, -206, -206, -206, -206, -206,
- /* 60 */ -206, -206, -206, -206, -206, -206, -206, -206, -206, -206,
- /* 70 */ -206, -206, 196, 309, 494, 537, 612, 656, 675, 679,
- /* 80 */ 681, 685, 724, 753, 771, 776, 788, 790, 794, 796,
- /* 90 */ 801, 803, 805, 807, 814, 819, 833, 837, 839, 842,
- /* 100 */ 845, 847, 849, 853, 873, 891, 893, 917, 921, 937,
- /* 110 */ 940, 944, 956, 960, 967, 969, 971, 973, 975, -206,
- /* 120 */ -206, -206, -206, -206, -206, -206, -206, -206, 501, -168,
- /* 130 */ 90, -97, 87, 112, 303, 277, 601, 277, 601, 179,
- /* 140 */ -206, -206, -206, -206, -107, -107, -107, -43, -56, 323,
- /* 150 */ 500, 512, -187, -177, 317, 609, 353, 353, 120, 144,
- /* 160 */ 490, 539, 698, 374, 467, 507, 789, 404, -157, 755,
- /* 170 */ 856, 916, 843, 941, 802, 770, 923, 821, 1001, -142,
- /* 180 */ 264, 785, 896, 905, 899, 949, -176, 544, 911, 953,
- /* 190 */ 1012, -182, -59, -30, 16, -22, 117, 172, 291, 369,
- /* 200 */ 407, 415, 566, 586, 647, 699, 754, 813, 850, 892,
- /* 210 */ 121, 1023, 1042, 1086, 1121, 1125, 1128, 1129, 1130, 1131,
- /* 220 */ 1132, 1134, 1135, 284, 1106, 1123, 1152, 1154, 1155, 1156,
- /* 230 */ 397, 1158, 1172, 1173, 1116, 1176, 1177, 1138, 1179, 117,
- /* 240 */ 1184, 1185, 1198, 1200, 1202, 1203, 741, 1094, 1153, 1146,
- /* 250 */ 1160, 1162, 1163, 397, 1153, 1153, 1170, 1204, 1206, 1103,
- /* 260 */ 1168, 1165, 1166, 1133, 1174, 1175, 1140, 1210, 1193, 1208,
- /* 270 */ 1212, 1215, 1216, 1178, 1167, 1189, 1196, 1241, 1148, 1243,
- /* 280 */ 1245, 1181, 1183, 1247, 1188, 1187, 1190, 1227, 1223, 1234,
- /* 290 */ 1236, 1238, 1239, 1274, 1278, 1235, 1237, 1213, 1218, 1253,
- /* 300 */ 1254, 1246, 1287, 1289, 1209, 1219, 1303, 1305, 1293, 1306,
- /* 310 */ 1309, 1313, 1316, 1297, 1301, 1307, 1308, 1298, 1310, 1311,
- /* 320 */ 1312, 1317, 1304, 1318, 1320, 1319, 1265, 1267, 1325, 1295,
- /* 330 */ 1300, 1296, 1302, 1326, 1321, 1327, 1330, 1365, 1323, 1269,
- /* 340 */ 1272, 1328, 1331, 1322, 1388, 1334, 1336, 1349, 1353, 1357,
- /* 350 */ 1405, 1409, 1420, 1421, 1427, 1429, 1430, 1332, 1335, 1339,
- /* 360 */ 1418, 1422, 1423, 1424, 1425, 1433, 1426, 1435, 1436, 1437,
- /* 370 */ 1438, 1441, 1439,
+ /* 0 */ -168, -17, 164, 214, 310, -166, -184, -18, 98, -170,
+ /* 10 */ 305, 315, -163, -193, -178, -257, 395, 401, 476, 478,
+ /* 20 */ 512, 117, 527, 529, 503, 509, 532, 255, 552, 556,
+ /* 30 */ 558, 607, 37, 408, 594, 413, 462, 559, 561, 601,
+ /* 40 */ 610, 618, -254, -254, -254, -254, -254, -254, -254, -254,
+ /* 50 */ -254, -254, -254, -254, -254, -254, -254, -254, -254, -254,
+ /* 60 */ -254, -254, -254, -254, -254, -254, -254, -254, -254, -254,
+ /* 70 */ -254, -254, -254, -254, -254, -254, -254, -111, 627, 650,
+ /* 80 */ 691, 697, 701, 703, 740, 742, 744, 767, 770, 790,
+ /* 90 */ 816, 818, 820, 822, 845, 857, 859, 861, 863, 865,
+ /* 100 */ 868, 870, 872, 874, 876, 888, 903, 906, 908, 915,
+ /* 110 */ 917, 922, 960, 962, 964, 988, 990, 992, 1015, 1017,
+ /* 120 */ 1029, 1033, 1035, 1040, -254, -254, -254, -254, -254, -254,
+ /* 130 */ -254, -254, -254, 190, 270, -196, 160, -160, 450, 647,
+ /* 140 */ 260, 458, 260, 458, 78, -254, -254, -254, -254, 206,
+ /* 150 */ 206, 206, 320, 598, -5, 675, 743, -148, 340, -125,
+ /* 160 */ 459, 466, 466, 693, -93, 461, 479, 706, 710, 714,
+ /* 170 */ 716, 717, 169, -183, 325, 314, 704, 333, 747, 858,
+ /* 180 */ -8, 819, 565, 755, 646, 660, 517, 265, 713, 791,
+ /* 190 */ 712, 795, 803, 918, 695, 860, 893, 935, 939, -181,
+ /* 200 */ -172, -147, -91, -46, -3, 162, 173, 231, 338, 437,
+ /* 210 */ 571, 614, 630, 651, 760, 931, 989, 1032, 1046, -218,
+ /* 220 */ 38, 1070, 1096, 1133, 1134, 1137, 1138, 1139, 1141, 1142,
+ /* 230 */ 1143, 1144, 292, 451, 1050, 1145, 1147, 1148, 1149, 813,
+ /* 240 */ 1161, 1162, 1163, 1108, 1049, 1166, 1168, 1146, 1169, 162,
+ /* 250 */ 1181, 1182, 1183, 1184, 1185, 1187, 1100, 1103, 1150, 1135,
+ /* 260 */ 1136, 1140, 1152, 813, 1150, 1150, 1153, 1173, 1195, 1090,
+ /* 270 */ 1154, 1167, 1170, 1104, 1155, 1156, 1109, 1172, 1174, 1179,
+ /* 280 */ 1177, 1188, 1205, 1171, 1160, 1196, 1121, 1165, 1203, 1228,
+ /* 290 */ 1157, 1244, 1245, 1158, 1159, 1250, 1175, 1193, 1191, 1241,
+ /* 300 */ 1231, 1243, 1257, 1259, 1261, 1276, 1280, 1254, 1255, 1230,
+ /* 310 */ 1234, 1269, 1270, 1260, 1292, 1303, 1223, 1225, 1309, 1313,
+ /* 320 */ 1296, 1317, 1319, 1320, 1323, 1300, 1307, 1308, 1310, 1304,
+ /* 330 */ 1311, 1315, 1314, 1318, 1316, 1321, 1325, 1324, 1271, 1272,
+ /* 340 */ 1332, 1294, 1298, 1299, 1301, 1302, 1306, 1312, 1329, 1356,
+ /* 350 */ 1256, 1263, 1327, 1326, 1305, 1369, 1330, 1340, 1343, 1346,
+ /* 360 */ 1350, 1391, 1394, 1410, 1412, 1415, 1417, 1419, 1328, 1331,
+ /* 370 */ 1335, 1402, 1398, 1400, 1401, 1403, 1408, 1396, 1397, 1409,
+ /* 380 */ 1414, 1420, 1421, 1413,
};
static const YYACTIONTYPE yy_default[] = {
- /* 0 */ 1500, 1500, 1500, 1346, 1129, 1235, 1129, 1129, 1129, 1346,
- /* 10 */ 1346, 1346, 1129, 1265, 1265, 1399, 1160, 1129, 1129, 1129,
- /* 20 */ 1129, 1129, 1129, 1129, 1345, 1129, 1129, 1129, 1129, 1129,
- /* 30 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1271, 1129,
- /* 40 */ 1129, 1129, 1129, 1129, 1347, 1348, 1129, 1129, 1129, 1398,
- /* 50 */ 1400, 1363, 1281, 1280, 1279, 1278, 1381, 1252, 1276, 1269,
- /* 60 */ 1273, 1341, 1342, 1340, 1344, 1348, 1347, 1129, 1272, 1312,
- /* 70 */ 1326, 1311, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129,
- /* 80 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129,
- /* 90 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129,
- /* 100 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129,
- /* 110 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1320,
- /* 120 */ 1325, 1331, 1324, 1321, 1314, 1313, 1315, 1316, 1129, 1150,
- /* 130 */ 1199, 1129, 1129, 1129, 1129, 1417, 1416, 1129, 1129, 1160,
- /* 140 */ 1317, 1318, 1328, 1327, 1406, 1456, 1455, 1364, 1129, 1129,
- /* 150 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129,
- /* 160 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129,
- /* 170 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129,
- /* 180 */ 1160, 1156, 1306, 1305, 1426, 1156, 1259, 1129, 1412, 1235,
- /* 190 */ 1226, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129,
- /* 200 */ 1129, 1129, 1129, 1129, 1403, 1401, 1129, 1129, 1129, 1129,
- /* 210 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129,
- /* 220 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129,
- /* 230 */ 1129, 1129, 1129, 1129, 1231, 1129, 1129, 1129, 1129, 1129,
- /* 240 */ 1129, 1129, 1129, 1129, 1129, 1450, 1129, 1376, 1213, 1231,
- /* 250 */ 1231, 1231, 1231, 1233, 1214, 1212, 1225, 1160, 1136, 1492,
- /* 260 */ 1275, 1254, 1254, 1489, 1275, 1275, 1489, 1174, 1470, 1171,
- /* 270 */ 1265, 1265, 1265, 1254, 1343, 1232, 1225, 1129, 1492, 1240,
- /* 280 */ 1240, 1491, 1491, 1240, 1364, 1284, 1290, 1202, 1275, 1208,
- /* 290 */ 1208, 1208, 1208, 1240, 1147, 1275, 1275, 1284, 1290, 1202,
- /* 300 */ 1202, 1275, 1240, 1147, 1380, 1486, 1240, 1147, 1354, 1240,
- /* 310 */ 1147, 1240, 1147, 1354, 1200, 1200, 1200, 1189, 1354, 1200,
- /* 320 */ 1174, 1200, 1189, 1200, 1200, 1354, 1358, 1358, 1354, 1258,
- /* 330 */ 1253, 1258, 1253, 1258, 1253, 1258, 1253, 1240, 1259, 1425,
- /* 340 */ 1129, 1270, 1259, 1349, 1240, 1129, 1270, 1268, 1266, 1275,
- /* 350 */ 1153, 1192, 1453, 1453, 1449, 1449, 1449, 1497, 1497, 1412,
- /* 360 */ 1465, 1160, 1160, 1160, 1160, 1465, 1176, 1176, 1160, 1160,
- /* 370 */ 1160, 1160, 1465, 1129, 1129, 1129, 1129, 1129, 1129, 1460,
- /* 380 */ 1129, 1365, 1244, 1129, 1129, 1129, 1129, 1129, 1129, 1129,
- /* 390 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129,
- /* 400 */ 1129, 1129, 1129, 1129, 1129, 1295, 1129, 1132, 1409, 1129,
- /* 410 */ 1129, 1407, 1129, 1129, 1129, 1129, 1129, 1129, 1245, 1129,
- /* 420 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129,
- /* 430 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1488, 1129, 1129,
- /* 440 */ 1129, 1129, 1129, 1129, 1379, 1378, 1129, 1129, 1242, 1129,
- /* 450 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129,
- /* 460 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129,
- /* 470 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129,
- /* 480 */ 1129, 1129, 1129, 1129, 1129, 1129, 1267, 1129, 1424, 1129,
- /* 490 */ 1129, 1129, 1129, 1129, 1129, 1129, 1438, 1260, 1129, 1129,
- /* 500 */ 1479, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129,
- /* 510 */ 1129, 1129, 1129, 1129, 1129, 1474, 1216, 1297, 1129, 1296,
- /* 520 */ 1300, 1129, 1141, 1129,
+ /* 0 */ 1536, 1536, 1536, 1376, 1159, 1265, 1159, 1159, 1159, 1376,
+ /* 10 */ 1376, 1376, 1159, 1295, 1295, 1429, 1190, 1159, 1159, 1159,
+ /* 20 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1375, 1159, 1159,
+ /* 30 */ 1159, 1159, 1459, 1459, 1159, 1159, 1159, 1159, 1159, 1159,
+ /* 40 */ 1159, 1159, 1159, 1301, 1159, 1159, 1159, 1159, 1159, 1377,
+ /* 50 */ 1378, 1159, 1159, 1159, 1428, 1430, 1393, 1311, 1310, 1309,
+ /* 60 */ 1308, 1411, 1282, 1306, 1299, 1303, 1371, 1372, 1370, 1374,
+ /* 70 */ 1378, 1377, 1159, 1302, 1342, 1356, 1341, 1159, 1159, 1159,
+ /* 80 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
+ /* 90 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
+ /* 100 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
+ /* 110 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
+ /* 120 */ 1159, 1159, 1159, 1159, 1350, 1355, 1361, 1354, 1351, 1344,
+ /* 130 */ 1343, 1345, 1346, 1159, 1180, 1229, 1159, 1159, 1159, 1159,
+ /* 140 */ 1447, 1446, 1159, 1159, 1190, 1347, 1348, 1358, 1357, 1436,
+ /* 150 */ 1492, 1491, 1394, 1159, 1159, 1159, 1159, 1159, 1159, 1459,
+ /* 160 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
+ /* 170 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
+ /* 180 */ 1159, 1159, 1159, 1159, 1459, 1459, 1159, 1190, 1459, 1459,
+ /* 190 */ 1186, 1336, 1335, 1186, 1289, 1159, 1442, 1265, 1256, 1159,
+ /* 200 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
+ /* 210 */ 1159, 1159, 1159, 1433, 1431, 1159, 1159, 1159, 1159, 1159,
+ /* 220 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
+ /* 230 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
+ /* 240 */ 1159, 1159, 1159, 1261, 1159, 1159, 1159, 1159, 1159, 1159,
+ /* 250 */ 1159, 1159, 1159, 1159, 1159, 1486, 1159, 1406, 1243, 1261,
+ /* 260 */ 1261, 1261, 1261, 1263, 1244, 1242, 1255, 1190, 1166, 1528,
+ /* 270 */ 1305, 1284, 1284, 1525, 1305, 1305, 1525, 1204, 1506, 1201,
+ /* 280 */ 1295, 1295, 1295, 1284, 1289, 1289, 1373, 1262, 1255, 1159,
+ /* 290 */ 1528, 1270, 1270, 1527, 1527, 1270, 1394, 1314, 1320, 1232,
+ /* 300 */ 1305, 1238, 1238, 1238, 1238, 1270, 1177, 1305, 1305, 1314,
+ /* 310 */ 1320, 1232, 1232, 1305, 1270, 1177, 1410, 1522, 1270, 1177,
+ /* 320 */ 1384, 1270, 1177, 1270, 1177, 1384, 1230, 1230, 1230, 1219,
+ /* 330 */ 1384, 1230, 1204, 1230, 1219, 1230, 1230, 1384, 1388, 1388,
+ /* 340 */ 1384, 1288, 1283, 1288, 1283, 1288, 1283, 1288, 1283, 1270,
+ /* 350 */ 1469, 1469, 1300, 1289, 1379, 1270, 1159, 1300, 1298, 1296,
+ /* 360 */ 1305, 1183, 1222, 1489, 1489, 1485, 1485, 1485, 1533, 1533,
+ /* 370 */ 1442, 1501, 1190, 1190, 1190, 1190, 1501, 1206, 1206, 1190,
+ /* 380 */ 1190, 1190, 1190, 1501, 1159, 1159, 1159, 1159, 1159, 1159,
+ /* 390 */ 1496, 1159, 1395, 1274, 1159, 1159, 1159, 1159, 1159, 1159,
+ /* 400 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
+ /* 410 */ 1159, 1159, 1159, 1159, 1159, 1159, 1325, 1159, 1162, 1439,
+ /* 420 */ 1159, 1159, 1437, 1159, 1159, 1159, 1159, 1159, 1159, 1275,
+ /* 430 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
+ /* 440 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1524, 1159,
+ /* 450 */ 1159, 1159, 1159, 1159, 1159, 1409, 1408, 1159, 1159, 1272,
+ /* 460 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
+ /* 470 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
+ /* 480 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
+ /* 490 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1297, 1159,
+ /* 500 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
+ /* 510 */ 1159, 1159, 1159, 1474, 1290, 1159, 1159, 1515, 1159, 1159,
+ /* 520 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
+ /* 530 */ 1159, 1159, 1510, 1246, 1327, 1159, 1326, 1330, 1159, 1171,
+ /* 540 */ 1159,
};
/********** End of lemon-generated parsing tables *****************************/
@@ -148346,6 +149183,10 @@ static const YYCODETYPE yyFallback[] = {
59, /* PRECEDING => ID */
59, /* RANGE => ID */
59, /* UNBOUNDED => ID */
+ 59, /* EXCLUDE => ID */
+ 59, /* GROUPS => ID */
+ 59, /* OTHERS => ID */
+ 59, /* TIES => ID */
59, /* REINDEX => ID */
59, /* RENAME => ID */
59, /* CTIME_KW => ID */
@@ -148524,196 +149365,219 @@ static const char *const yyTokenName[] = {
/* 85 */ "PRECEDING",
/* 86 */ "RANGE",
/* 87 */ "UNBOUNDED",
- /* 88 */ "REINDEX",
- /* 89 */ "RENAME",
- /* 90 */ "CTIME_KW",
- /* 91 */ "ANY",
- /* 92 */ "BITAND",
- /* 93 */ "BITOR",
- /* 94 */ "LSHIFT",
- /* 95 */ "RSHIFT",
- /* 96 */ "PLUS",
- /* 97 */ "MINUS",
- /* 98 */ "STAR",
- /* 99 */ "SLASH",
- /* 100 */ "REM",
- /* 101 */ "CONCAT",
- /* 102 */ "COLLATE",
- /* 103 */ "BITNOT",
- /* 104 */ "ON",
- /* 105 */ "INDEXED",
- /* 106 */ "STRING",
- /* 107 */ "JOIN_KW",
- /* 108 */ "CONSTRAINT",
- /* 109 */ "DEFAULT",
- /* 110 */ "NULL",
- /* 111 */ "PRIMARY",
- /* 112 */ "UNIQUE",
- /* 113 */ "CHECK",
- /* 114 */ "REFERENCES",
- /* 115 */ "AUTOINCR",
- /* 116 */ "INSERT",
- /* 117 */ "DELETE",
- /* 118 */ "UPDATE",
- /* 119 */ "SET",
- /* 120 */ "DEFERRABLE",
- /* 121 */ "FOREIGN",
- /* 122 */ "DROP",
- /* 123 */ "UNION",
- /* 124 */ "ALL",
- /* 125 */ "EXCEPT",
- /* 126 */ "INTERSECT",
- /* 127 */ "SELECT",
- /* 128 */ "VALUES",
- /* 129 */ "DISTINCT",
- /* 130 */ "DOT",
- /* 131 */ "FROM",
- /* 132 */ "JOIN",
- /* 133 */ "USING",
- /* 134 */ "ORDER",
- /* 135 */ "GROUP",
- /* 136 */ "HAVING",
- /* 137 */ "LIMIT",
- /* 138 */ "WHERE",
- /* 139 */ "INTO",
- /* 140 */ "NOTHING",
- /* 141 */ "FLOAT",
- /* 142 */ "BLOB",
- /* 143 */ "INTEGER",
- /* 144 */ "VARIABLE",
- /* 145 */ "CASE",
- /* 146 */ "WHEN",
- /* 147 */ "THEN",
- /* 148 */ "ELSE",
- /* 149 */ "INDEX",
- /* 150 */ "ALTER",
- /* 151 */ "ADD",
- /* 152 */ "WINDOW",
- /* 153 */ "OVER",
- /* 154 */ "FILTER",
- /* 155 */ "input",
- /* 156 */ "cmdlist",
- /* 157 */ "ecmd",
- /* 158 */ "cmdx",
- /* 159 */ "explain",
- /* 160 */ "cmd",
- /* 161 */ "transtype",
- /* 162 */ "trans_opt",
- /* 163 */ "nm",
- /* 164 */ "savepoint_opt",
- /* 165 */ "create_table",
- /* 166 */ "create_table_args",
- /* 167 */ "createkw",
- /* 168 */ "temp",
- /* 169 */ "ifnotexists",
- /* 170 */ "dbnm",
- /* 171 */ "columnlist",
- /* 172 */ "conslist_opt",
- /* 173 */ "table_options",
- /* 174 */ "select",
- /* 175 */ "columnname",
- /* 176 */ "carglist",
- /* 177 */ "typetoken",
- /* 178 */ "typename",
- /* 179 */ "signed",
- /* 180 */ "plus_num",
- /* 181 */ "minus_num",
- /* 182 */ "scanpt",
- /* 183 */ "ccons",
- /* 184 */ "term",
- /* 185 */ "expr",
- /* 186 */ "onconf",
- /* 187 */ "sortorder",
- /* 188 */ "autoinc",
- /* 189 */ "eidlist_opt",
- /* 190 */ "refargs",
- /* 191 */ "defer_subclause",
- /* 192 */ "refarg",
- /* 193 */ "refact",
- /* 194 */ "init_deferred_pred_opt",
- /* 195 */ "conslist",
- /* 196 */ "tconscomma",
- /* 197 */ "tcons",
- /* 198 */ "sortlist",
- /* 199 */ "eidlist",
- /* 200 */ "defer_subclause_opt",
- /* 201 */ "orconf",
- /* 202 */ "resolvetype",
- /* 203 */ "raisetype",
- /* 204 */ "ifexists",
- /* 205 */ "fullname",
- /* 206 */ "selectnowith",
- /* 207 */ "oneselect",
- /* 208 */ "wqlist",
- /* 209 */ "multiselect_op",
- /* 210 */ "distinct",
- /* 211 */ "selcollist",
- /* 212 */ "from",
- /* 213 */ "where_opt",
- /* 214 */ "groupby_opt",
- /* 215 */ "having_opt",
- /* 216 */ "orderby_opt",
- /* 217 */ "limit_opt",
- /* 218 */ "window_clause",
- /* 219 */ "values",
- /* 220 */ "nexprlist",
- /* 221 */ "sclp",
- /* 222 */ "as",
- /* 223 */ "seltablist",
- /* 224 */ "stl_prefix",
- /* 225 */ "joinop",
- /* 226 */ "indexed_opt",
- /* 227 */ "on_opt",
- /* 228 */ "using_opt",
- /* 229 */ "exprlist",
- /* 230 */ "xfullname",
- /* 231 */ "idlist",
- /* 232 */ "with",
- /* 233 */ "setlist",
- /* 234 */ "insert_cmd",
- /* 235 */ "idlist_opt",
- /* 236 */ "upsert",
- /* 237 */ "over_clause",
- /* 238 */ "likeop",
- /* 239 */ "between_op",
- /* 240 */ "in_op",
- /* 241 */ "paren_exprlist",
- /* 242 */ "case_operand",
- /* 243 */ "case_exprlist",
- /* 244 */ "case_else",
- /* 245 */ "uniqueflag",
- /* 246 */ "collate",
- /* 247 */ "vinto",
- /* 248 */ "nmnum",
- /* 249 */ "trigger_decl",
- /* 250 */ "trigger_cmd_list",
- /* 251 */ "trigger_time",
- /* 252 */ "trigger_event",
- /* 253 */ "foreach_clause",
- /* 254 */ "when_clause",
- /* 255 */ "trigger_cmd",
- /* 256 */ "trnm",
- /* 257 */ "tridxby",
- /* 258 */ "database_kw_opt",
- /* 259 */ "key_opt",
- /* 260 */ "add_column_fullname",
- /* 261 */ "kwcolumn_opt",
- /* 262 */ "create_vtab",
- /* 263 */ "vtabarglist",
- /* 264 */ "vtabarg",
- /* 265 */ "vtabargtoken",
- /* 266 */ "lp",
- /* 267 */ "anylist",
- /* 268 */ "windowdefn_list",
- /* 269 */ "windowdefn",
- /* 270 */ "window",
- /* 271 */ "frame_opt",
- /* 272 */ "part_opt",
- /* 273 */ "filter_opt",
- /* 274 */ "range_or_rows",
- /* 275 */ "frame_bound",
- /* 276 */ "frame_bound_s",
- /* 277 */ "frame_bound_e",
+ /* 88 */ "EXCLUDE",
+ /* 89 */ "GROUPS",
+ /* 90 */ "OTHERS",
+ /* 91 */ "TIES",
+ /* 92 */ "REINDEX",
+ /* 93 */ "RENAME",
+ /* 94 */ "CTIME_KW",
+ /* 95 */ "ANY",
+ /* 96 */ "BITAND",
+ /* 97 */ "BITOR",
+ /* 98 */ "LSHIFT",
+ /* 99 */ "RSHIFT",
+ /* 100 */ "PLUS",
+ /* 101 */ "MINUS",
+ /* 102 */ "STAR",
+ /* 103 */ "SLASH",
+ /* 104 */ "REM",
+ /* 105 */ "CONCAT",
+ /* 106 */ "COLLATE",
+ /* 107 */ "BITNOT",
+ /* 108 */ "ON",
+ /* 109 */ "INDEXED",
+ /* 110 */ "STRING",
+ /* 111 */ "JOIN_KW",
+ /* 112 */ "CONSTRAINT",
+ /* 113 */ "DEFAULT",
+ /* 114 */ "NULL",
+ /* 115 */ "PRIMARY",
+ /* 116 */ "UNIQUE",
+ /* 117 */ "CHECK",
+ /* 118 */ "REFERENCES",
+ /* 119 */ "AUTOINCR",
+ /* 120 */ "INSERT",
+ /* 121 */ "DELETE",
+ /* 122 */ "UPDATE",
+ /* 123 */ "SET",
+ /* 124 */ "DEFERRABLE",
+ /* 125 */ "FOREIGN",
+ /* 126 */ "DROP",
+ /* 127 */ "UNION",
+ /* 128 */ "ALL",
+ /* 129 */ "EXCEPT",
+ /* 130 */ "INTERSECT",
+ /* 131 */ "SELECT",
+ /* 132 */ "VALUES",
+ /* 133 */ "DISTINCT",
+ /* 134 */ "DOT",
+ /* 135 */ "FROM",
+ /* 136 */ "JOIN",
+ /* 137 */ "USING",
+ /* 138 */ "ORDER",
+ /* 139 */ "GROUP",
+ /* 140 */ "HAVING",
+ /* 141 */ "LIMIT",
+ /* 142 */ "WHERE",
+ /* 143 */ "INTO",
+ /* 144 */ "NOTHING",
+ /* 145 */ "FLOAT",
+ /* 146 */ "BLOB",
+ /* 147 */ "INTEGER",
+ /* 148 */ "VARIABLE",
+ /* 149 */ "CASE",
+ /* 150 */ "WHEN",
+ /* 151 */ "THEN",
+ /* 152 */ "ELSE",
+ /* 153 */ "INDEX",
+ /* 154 */ "ALTER",
+ /* 155 */ "ADD",
+ /* 156 */ "WINDOW",
+ /* 157 */ "OVER",
+ /* 158 */ "FILTER",
+ /* 159 */ "TRUEFALSE",
+ /* 160 */ "ISNOT",
+ /* 161 */ "FUNCTION",
+ /* 162 */ "COLUMN",
+ /* 163 */ "AGG_FUNCTION",
+ /* 164 */ "AGG_COLUMN",
+ /* 165 */ "UMINUS",
+ /* 166 */ "UPLUS",
+ /* 167 */ "TRUTH",
+ /* 168 */ "REGISTER",
+ /* 169 */ "VECTOR",
+ /* 170 */ "SELECT_COLUMN",
+ /* 171 */ "IF_NULL_ROW",
+ /* 172 */ "ASTERISK",
+ /* 173 */ "SPAN",
+ /* 174 */ "SPACE",
+ /* 175 */ "ILLEGAL",
+ /* 176 */ "input",
+ /* 177 */ "cmdlist",
+ /* 178 */ "ecmd",
+ /* 179 */ "cmdx",
+ /* 180 */ "explain",
+ /* 181 */ "cmd",
+ /* 182 */ "transtype",
+ /* 183 */ "trans_opt",
+ /* 184 */ "nm",
+ /* 185 */ "savepoint_opt",
+ /* 186 */ "create_table",
+ /* 187 */ "create_table_args",
+ /* 188 */ "createkw",
+ /* 189 */ "temp",
+ /* 190 */ "ifnotexists",
+ /* 191 */ "dbnm",
+ /* 192 */ "columnlist",
+ /* 193 */ "conslist_opt",
+ /* 194 */ "table_options",
+ /* 195 */ "select",
+ /* 196 */ "columnname",
+ /* 197 */ "carglist",
+ /* 198 */ "typetoken",
+ /* 199 */ "typename",
+ /* 200 */ "signed",
+ /* 201 */ "plus_num",
+ /* 202 */ "minus_num",
+ /* 203 */ "scanpt",
+ /* 204 */ "ccons",
+ /* 205 */ "term",
+ /* 206 */ "expr",
+ /* 207 */ "onconf",
+ /* 208 */ "sortorder",
+ /* 209 */ "autoinc",
+ /* 210 */ "eidlist_opt",
+ /* 211 */ "refargs",
+ /* 212 */ "defer_subclause",
+ /* 213 */ "refarg",
+ /* 214 */ "refact",
+ /* 215 */ "init_deferred_pred_opt",
+ /* 216 */ "conslist",
+ /* 217 */ "tconscomma",
+ /* 218 */ "tcons",
+ /* 219 */ "sortlist",
+ /* 220 */ "eidlist",
+ /* 221 */ "defer_subclause_opt",
+ /* 222 */ "orconf",
+ /* 223 */ "resolvetype",
+ /* 224 */ "raisetype",
+ /* 225 */ "ifexists",
+ /* 226 */ "fullname",
+ /* 227 */ "selectnowith",
+ /* 228 */ "oneselect",
+ /* 229 */ "wqlist",
+ /* 230 */ "multiselect_op",
+ /* 231 */ "distinct",
+ /* 232 */ "selcollist",
+ /* 233 */ "from",
+ /* 234 */ "where_opt",
+ /* 235 */ "groupby_opt",
+ /* 236 */ "having_opt",
+ /* 237 */ "orderby_opt",
+ /* 238 */ "limit_opt",
+ /* 239 */ "window_clause",
+ /* 240 */ "values",
+ /* 241 */ "nexprlist",
+ /* 242 */ "sclp",
+ /* 243 */ "as",
+ /* 244 */ "seltablist",
+ /* 245 */ "stl_prefix",
+ /* 246 */ "joinop",
+ /* 247 */ "indexed_opt",
+ /* 248 */ "on_opt",
+ /* 249 */ "using_opt",
+ /* 250 */ "exprlist",
+ /* 251 */ "xfullname",
+ /* 252 */ "idlist",
+ /* 253 */ "with",
+ /* 254 */ "setlist",
+ /* 255 */ "insert_cmd",
+ /* 256 */ "idlist_opt",
+ /* 257 */ "upsert",
+ /* 258 */ "over_clause",
+ /* 259 */ "likeop",
+ /* 260 */ "between_op",
+ /* 261 */ "in_op",
+ /* 262 */ "paren_exprlist",
+ /* 263 */ "case_operand",
+ /* 264 */ "case_exprlist",
+ /* 265 */ "case_else",
+ /* 266 */ "uniqueflag",
+ /* 267 */ "collate",
+ /* 268 */ "vinto",
+ /* 269 */ "nmnum",
+ /* 270 */ "trigger_decl",
+ /* 271 */ "trigger_cmd_list",
+ /* 272 */ "trigger_time",
+ /* 273 */ "trigger_event",
+ /* 274 */ "foreach_clause",
+ /* 275 */ "when_clause",
+ /* 276 */ "trigger_cmd",
+ /* 277 */ "trnm",
+ /* 278 */ "tridxby",
+ /* 279 */ "database_kw_opt",
+ /* 280 */ "key_opt",
+ /* 281 */ "add_column_fullname",
+ /* 282 */ "kwcolumn_opt",
+ /* 283 */ "create_vtab",
+ /* 284 */ "vtabarglist",
+ /* 285 */ "vtabarg",
+ /* 286 */ "vtabargtoken",
+ /* 287 */ "lp",
+ /* 288 */ "anylist",
+ /* 289 */ "windowdefn_list",
+ /* 290 */ "windowdefn",
+ /* 291 */ "window",
+ /* 292 */ "frame_opt",
+ /* 293 */ "part_opt",
+ /* 294 */ "filter_opt",
+ /* 295 */ "range_or_rows",
+ /* 296 */ "frame_bound",
+ /* 297 */ "frame_bound_s",
+ /* 298 */ "frame_bound_e",
+ /* 299 */ "frame_exclude_opt",
+ /* 300 */ "frame_exclude",
};
#endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */
@@ -149011,85 +149875,91 @@ static const char *const yyRuleName[] = {
/* 287 */ "wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP",
/* 288 */ "windowdefn_list ::= windowdefn",
/* 289 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn",
- /* 290 */ "windowdefn ::= nm AS window",
- /* 291 */ "window ::= LP part_opt orderby_opt frame_opt RP",
- /* 292 */ "part_opt ::= PARTITION BY nexprlist",
- /* 293 */ "part_opt ::=",
- /* 294 */ "frame_opt ::=",
- /* 295 */ "frame_opt ::= range_or_rows frame_bound_s",
- /* 296 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e",
- /* 297 */ "range_or_rows ::= RANGE",
- /* 298 */ "range_or_rows ::= ROWS",
- /* 299 */ "frame_bound_s ::= frame_bound",
- /* 300 */ "frame_bound_s ::= UNBOUNDED PRECEDING",
- /* 301 */ "frame_bound_e ::= frame_bound",
- /* 302 */ "frame_bound_e ::= UNBOUNDED FOLLOWING",
- /* 303 */ "frame_bound ::= expr PRECEDING",
- /* 304 */ "frame_bound ::= CURRENT ROW",
- /* 305 */ "frame_bound ::= expr FOLLOWING",
- /* 306 */ "window_clause ::= WINDOW windowdefn_list",
- /* 307 */ "over_clause ::= filter_opt OVER window",
- /* 308 */ "over_clause ::= filter_opt OVER nm",
- /* 309 */ "filter_opt ::=",
- /* 310 */ "filter_opt ::= FILTER LP WHERE expr RP",
- /* 311 */ "input ::= cmdlist",
- /* 312 */ "cmdlist ::= cmdlist ecmd",
- /* 313 */ "cmdlist ::= ecmd",
- /* 314 */ "ecmd ::= SEMI",
- /* 315 */ "ecmd ::= cmdx SEMI",
- /* 316 */ "ecmd ::= explain cmdx",
- /* 317 */ "trans_opt ::=",
- /* 318 */ "trans_opt ::= TRANSACTION",
- /* 319 */ "trans_opt ::= TRANSACTION nm",
- /* 320 */ "savepoint_opt ::= SAVEPOINT",
- /* 321 */ "savepoint_opt ::=",
- /* 322 */ "cmd ::= create_table create_table_args",
- /* 323 */ "columnlist ::= columnlist COMMA columnname carglist",
- /* 324 */ "columnlist ::= columnname carglist",
- /* 325 */ "nm ::= ID|INDEXED",
- /* 326 */ "nm ::= STRING",
- /* 327 */ "nm ::= JOIN_KW",
- /* 328 */ "typetoken ::= typename",
- /* 329 */ "typename ::= ID|STRING",
- /* 330 */ "signed ::= plus_num",
- /* 331 */ "signed ::= minus_num",
- /* 332 */ "carglist ::= carglist ccons",
- /* 333 */ "carglist ::=",
- /* 334 */ "ccons ::= NULL onconf",
- /* 335 */ "conslist_opt ::= COMMA conslist",
- /* 336 */ "conslist ::= conslist tconscomma tcons",
- /* 337 */ "conslist ::= tcons",
- /* 338 */ "tconscomma ::=",
- /* 339 */ "defer_subclause_opt ::= defer_subclause",
- /* 340 */ "resolvetype ::= raisetype",
- /* 341 */ "selectnowith ::= oneselect",
- /* 342 */ "oneselect ::= values",
- /* 343 */ "sclp ::= selcollist COMMA",
- /* 344 */ "as ::= ID|STRING",
- /* 345 */ "expr ::= term",
- /* 346 */ "likeop ::= LIKE_KW|MATCH",
- /* 347 */ "exprlist ::= nexprlist",
- /* 348 */ "nmnum ::= plus_num",
- /* 349 */ "nmnum ::= nm",
- /* 350 */ "nmnum ::= ON",
- /* 351 */ "nmnum ::= DELETE",
- /* 352 */ "nmnum ::= DEFAULT",
- /* 353 */ "plus_num ::= INTEGER|FLOAT",
- /* 354 */ "foreach_clause ::=",
- /* 355 */ "foreach_clause ::= FOR EACH ROW",
- /* 356 */ "trnm ::= nm",
- /* 357 */ "tridxby ::=",
- /* 358 */ "database_kw_opt ::= DATABASE",
- /* 359 */ "database_kw_opt ::=",
- /* 360 */ "kwcolumn_opt ::=",
- /* 361 */ "kwcolumn_opt ::= COLUMNKW",
- /* 362 */ "vtabarglist ::= vtabarg",
- /* 363 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
- /* 364 */ "vtabarg ::= vtabarg vtabargtoken",
- /* 365 */ "anylist ::=",
- /* 366 */ "anylist ::= anylist LP anylist RP",
- /* 367 */ "anylist ::= anylist ANY",
- /* 368 */ "with ::=",
+ /* 290 */ "windowdefn ::= nm AS LP window RP",
+ /* 291 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt",
+ /* 292 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt",
+ /* 293 */ "window ::= ORDER BY sortlist frame_opt",
+ /* 294 */ "window ::= nm ORDER BY sortlist frame_opt",
+ /* 295 */ "window ::= frame_opt",
+ /* 296 */ "window ::= nm frame_opt",
+ /* 297 */ "frame_opt ::=",
+ /* 298 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt",
+ /* 299 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt",
+ /* 300 */ "range_or_rows ::= RANGE|ROWS|GROUPS",
+ /* 301 */ "frame_bound_s ::= frame_bound",
+ /* 302 */ "frame_bound_s ::= UNBOUNDED PRECEDING",
+ /* 303 */ "frame_bound_e ::= frame_bound",
+ /* 304 */ "frame_bound_e ::= UNBOUNDED FOLLOWING",
+ /* 305 */ "frame_bound ::= expr PRECEDING|FOLLOWING",
+ /* 306 */ "frame_bound ::= CURRENT ROW",
+ /* 307 */ "frame_exclude_opt ::=",
+ /* 308 */ "frame_exclude_opt ::= EXCLUDE frame_exclude",
+ /* 309 */ "frame_exclude ::= NO OTHERS",
+ /* 310 */ "frame_exclude ::= CURRENT ROW",
+ /* 311 */ "frame_exclude ::= GROUP|TIES",
+ /* 312 */ "window_clause ::= WINDOW windowdefn_list",
+ /* 313 */ "over_clause ::= filter_opt OVER LP window RP",
+ /* 314 */ "over_clause ::= filter_opt OVER nm",
+ /* 315 */ "filter_opt ::=",
+ /* 316 */ "filter_opt ::= FILTER LP WHERE expr RP",
+ /* 317 */ "input ::= cmdlist",
+ /* 318 */ "cmdlist ::= cmdlist ecmd",
+ /* 319 */ "cmdlist ::= ecmd",
+ /* 320 */ "ecmd ::= SEMI",
+ /* 321 */ "ecmd ::= cmdx SEMI",
+ /* 322 */ "ecmd ::= explain cmdx",
+ /* 323 */ "trans_opt ::=",
+ /* 324 */ "trans_opt ::= TRANSACTION",
+ /* 325 */ "trans_opt ::= TRANSACTION nm",
+ /* 326 */ "savepoint_opt ::= SAVEPOINT",
+ /* 327 */ "savepoint_opt ::=",
+ /* 328 */ "cmd ::= create_table create_table_args",
+ /* 329 */ "columnlist ::= columnlist COMMA columnname carglist",
+ /* 330 */ "columnlist ::= columnname carglist",
+ /* 331 */ "nm ::= ID|INDEXED",
+ /* 332 */ "nm ::= STRING",
+ /* 333 */ "nm ::= JOIN_KW",
+ /* 334 */ "typetoken ::= typename",
+ /* 335 */ "typename ::= ID|STRING",
+ /* 336 */ "signed ::= plus_num",
+ /* 337 */ "signed ::= minus_num",
+ /* 338 */ "carglist ::= carglist ccons",
+ /* 339 */ "carglist ::=",
+ /* 340 */ "ccons ::= NULL onconf",
+ /* 341 */ "conslist_opt ::= COMMA conslist",
+ /* 342 */ "conslist ::= conslist tconscomma tcons",
+ /* 343 */ "conslist ::= tcons",
+ /* 344 */ "tconscomma ::=",
+ /* 345 */ "defer_subclause_opt ::= defer_subclause",
+ /* 346 */ "resolvetype ::= raisetype",
+ /* 347 */ "selectnowith ::= oneselect",
+ /* 348 */ "oneselect ::= values",
+ /* 349 */ "sclp ::= selcollist COMMA",
+ /* 350 */ "as ::= ID|STRING",
+ /* 351 */ "expr ::= term",
+ /* 352 */ "likeop ::= LIKE_KW|MATCH",
+ /* 353 */ "exprlist ::= nexprlist",
+ /* 354 */ "nmnum ::= plus_num",
+ /* 355 */ "nmnum ::= nm",
+ /* 356 */ "nmnum ::= ON",
+ /* 357 */ "nmnum ::= DELETE",
+ /* 358 */ "nmnum ::= DEFAULT",
+ /* 359 */ "plus_num ::= INTEGER|FLOAT",
+ /* 360 */ "foreach_clause ::=",
+ /* 361 */ "foreach_clause ::= FOR EACH ROW",
+ /* 362 */ "trnm ::= nm",
+ /* 363 */ "tridxby ::=",
+ /* 364 */ "database_kw_opt ::= DATABASE",
+ /* 365 */ "database_kw_opt ::=",
+ /* 366 */ "kwcolumn_opt ::=",
+ /* 367 */ "kwcolumn_opt ::= COLUMNKW",
+ /* 368 */ "vtabarglist ::= vtabarg",
+ /* 369 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
+ /* 370 */ "vtabarg ::= vtabarg vtabargtoken",
+ /* 371 */ "anylist ::=",
+ /* 372 */ "anylist ::= anylist LP anylist RP",
+ /* 373 */ "anylist ::= anylist ANY",
+ /* 374 */ "with ::=",
};
#endif /* NDEBUG */
@@ -149215,97 +150085,97 @@ static void yy_destructor(
** inside the C code.
*/
/********* Begin destructor definitions ***************************************/
- case 174: /* select */
- case 206: /* selectnowith */
- case 207: /* oneselect */
- case 219: /* values */
+ case 195: /* select */
+ case 227: /* selectnowith */
+ case 228: /* oneselect */
+ case 240: /* values */
{
-sqlite3SelectDelete(pParse->db, (yypminor->yy423));
+sqlite3SelectDelete(pParse->db, (yypminor->yy457));
}
break;
- case 184: /* term */
- case 185: /* expr */
- case 213: /* where_opt */
- case 215: /* having_opt */
- case 227: /* on_opt */
- case 242: /* case_operand */
- case 244: /* case_else */
- case 247: /* vinto */
- case 254: /* when_clause */
- case 259: /* key_opt */
- case 273: /* filter_opt */
+ case 205: /* term */
+ case 206: /* expr */
+ case 234: /* where_opt */
+ case 236: /* having_opt */
+ case 248: /* on_opt */
+ case 263: /* case_operand */
+ case 265: /* case_else */
+ case 268: /* vinto */
+ case 275: /* when_clause */
+ case 280: /* key_opt */
+ case 294: /* filter_opt */
{
-sqlite3ExprDelete(pParse->db, (yypminor->yy490));
+sqlite3ExprDelete(pParse->db, (yypminor->yy524));
}
break;
- case 189: /* eidlist_opt */
- case 198: /* sortlist */
- case 199: /* eidlist */
- case 211: /* selcollist */
- case 214: /* groupby_opt */
- case 216: /* orderby_opt */
- case 220: /* nexprlist */
- case 221: /* sclp */
- case 229: /* exprlist */
- case 233: /* setlist */
- case 241: /* paren_exprlist */
- case 243: /* case_exprlist */
- case 272: /* part_opt */
+ case 210: /* eidlist_opt */
+ case 219: /* sortlist */
+ case 220: /* eidlist */
+ case 232: /* selcollist */
+ case 235: /* groupby_opt */
+ case 237: /* orderby_opt */
+ case 241: /* nexprlist */
+ case 242: /* sclp */
+ case 250: /* exprlist */
+ case 254: /* setlist */
+ case 262: /* paren_exprlist */
+ case 264: /* case_exprlist */
+ case 293: /* part_opt */
{
-sqlite3ExprListDelete(pParse->db, (yypminor->yy42));
+sqlite3ExprListDelete(pParse->db, (yypminor->yy434));
}
break;
- case 205: /* fullname */
- case 212: /* from */
- case 223: /* seltablist */
- case 224: /* stl_prefix */
- case 230: /* xfullname */
+ case 226: /* fullname */
+ case 233: /* from */
+ case 244: /* seltablist */
+ case 245: /* stl_prefix */
+ case 251: /* xfullname */
{
-sqlite3SrcListDelete(pParse->db, (yypminor->yy167));
+sqlite3SrcListDelete(pParse->db, (yypminor->yy483));
}
break;
- case 208: /* wqlist */
+ case 229: /* wqlist */
{
-sqlite3WithDelete(pParse->db, (yypminor->yy499));
+sqlite3WithDelete(pParse->db, (yypminor->yy59));
}
break;
- case 218: /* window_clause */
- case 268: /* windowdefn_list */
+ case 239: /* window_clause */
+ case 289: /* windowdefn_list */
{
-sqlite3WindowListDelete(pParse->db, (yypminor->yy147));
+sqlite3WindowListDelete(pParse->db, (yypminor->yy295));
}
break;
- case 228: /* using_opt */
- case 231: /* idlist */
- case 235: /* idlist_opt */
+ case 249: /* using_opt */
+ case 252: /* idlist */
+ case 256: /* idlist_opt */
{
-sqlite3IdListDelete(pParse->db, (yypminor->yy336));
+sqlite3IdListDelete(pParse->db, (yypminor->yy62));
}
break;
- case 237: /* over_clause */
- case 269: /* windowdefn */
- case 270: /* window */
- case 271: /* frame_opt */
+ case 258: /* over_clause */
+ case 290: /* windowdefn */
+ case 291: /* window */
+ case 292: /* frame_opt */
{
-sqlite3WindowDelete(pParse->db, (yypminor->yy147));
+sqlite3WindowDelete(pParse->db, (yypminor->yy295));
}
break;
- case 250: /* trigger_cmd_list */
- case 255: /* trigger_cmd */
+ case 271: /* trigger_cmd_list */
+ case 276: /* trigger_cmd */
{
-sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy119));
+sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy455));
}
break;
- case 252: /* trigger_event */
+ case 273: /* trigger_event */
{
-sqlite3IdListDelete(pParse->db, (yypminor->yy350).b);
+sqlite3IdListDelete(pParse->db, (yypminor->yy90).b);
}
break;
- case 275: /* frame_bound */
- case 276: /* frame_bound_s */
- case 277: /* frame_bound_e */
+ case 296: /* frame_bound */
+ case 297: /* frame_bound_s */
+ case 298: /* frame_bound_e */
{
-sqlite3ExprDelete(pParse->db, (yypminor->yy317).pExpr);
+sqlite3ExprDelete(pParse->db, (yypminor->yy201).pExpr);
}
break;
/********* End destructor definitions *****************************************/
@@ -149600,375 +150470,381 @@ static void yy_shift(
/* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side
** of that rule */
static const YYCODETYPE yyRuleInfoLhs[] = {
- 159, /* (0) explain ::= EXPLAIN */
- 159, /* (1) explain ::= EXPLAIN QUERY PLAN */
- 158, /* (2) cmdx ::= cmd */
- 160, /* (3) cmd ::= BEGIN transtype trans_opt */
- 161, /* (4) transtype ::= */
- 161, /* (5) transtype ::= DEFERRED */
- 161, /* (6) transtype ::= IMMEDIATE */
- 161, /* (7) transtype ::= EXCLUSIVE */
- 160, /* (8) cmd ::= COMMIT|END trans_opt */
- 160, /* (9) cmd ::= ROLLBACK trans_opt */
- 160, /* (10) cmd ::= SAVEPOINT nm */
- 160, /* (11) cmd ::= RELEASE savepoint_opt nm */
- 160, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */
- 165, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */
- 167, /* (14) createkw ::= CREATE */
- 169, /* (15) ifnotexists ::= */
- 169, /* (16) ifnotexists ::= IF NOT EXISTS */
- 168, /* (17) temp ::= TEMP */
- 168, /* (18) temp ::= */
- 166, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_options */
- 166, /* (20) create_table_args ::= AS select */
- 173, /* (21) table_options ::= */
- 173, /* (22) table_options ::= WITHOUT nm */
- 175, /* (23) columnname ::= nm typetoken */
- 177, /* (24) typetoken ::= */
- 177, /* (25) typetoken ::= typename LP signed RP */
- 177, /* (26) typetoken ::= typename LP signed COMMA signed RP */
- 178, /* (27) typename ::= typename ID|STRING */
- 182, /* (28) scanpt ::= */
- 183, /* (29) ccons ::= CONSTRAINT nm */
- 183, /* (30) ccons ::= DEFAULT scanpt term scanpt */
- 183, /* (31) ccons ::= DEFAULT LP expr RP */
- 183, /* (32) ccons ::= DEFAULT PLUS term scanpt */
- 183, /* (33) ccons ::= DEFAULT MINUS term scanpt */
- 183, /* (34) ccons ::= DEFAULT scanpt ID|INDEXED */
- 183, /* (35) ccons ::= NOT NULL onconf */
- 183, /* (36) ccons ::= PRIMARY KEY sortorder onconf autoinc */
- 183, /* (37) ccons ::= UNIQUE onconf */
- 183, /* (38) ccons ::= CHECK LP expr RP */
- 183, /* (39) ccons ::= REFERENCES nm eidlist_opt refargs */
- 183, /* (40) ccons ::= defer_subclause */
- 183, /* (41) ccons ::= COLLATE ID|STRING */
- 188, /* (42) autoinc ::= */
- 188, /* (43) autoinc ::= AUTOINCR */
- 190, /* (44) refargs ::= */
- 190, /* (45) refargs ::= refargs refarg */
- 192, /* (46) refarg ::= MATCH nm */
- 192, /* (47) refarg ::= ON INSERT refact */
- 192, /* (48) refarg ::= ON DELETE refact */
- 192, /* (49) refarg ::= ON UPDATE refact */
- 193, /* (50) refact ::= SET NULL */
- 193, /* (51) refact ::= SET DEFAULT */
- 193, /* (52) refact ::= CASCADE */
- 193, /* (53) refact ::= RESTRICT */
- 193, /* (54) refact ::= NO ACTION */
- 191, /* (55) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
- 191, /* (56) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
- 194, /* (57) init_deferred_pred_opt ::= */
- 194, /* (58) init_deferred_pred_opt ::= INITIALLY DEFERRED */
- 194, /* (59) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
- 172, /* (60) conslist_opt ::= */
- 196, /* (61) tconscomma ::= COMMA */
- 197, /* (62) tcons ::= CONSTRAINT nm */
- 197, /* (63) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
- 197, /* (64) tcons ::= UNIQUE LP sortlist RP onconf */
- 197, /* (65) tcons ::= CHECK LP expr RP onconf */
- 197, /* (66) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
- 200, /* (67) defer_subclause_opt ::= */
- 186, /* (68) onconf ::= */
- 186, /* (69) onconf ::= ON CONFLICT resolvetype */
- 201, /* (70) orconf ::= */
- 201, /* (71) orconf ::= OR resolvetype */
- 202, /* (72) resolvetype ::= IGNORE */
- 202, /* (73) resolvetype ::= REPLACE */
- 160, /* (74) cmd ::= DROP TABLE ifexists fullname */
- 204, /* (75) ifexists ::= IF EXISTS */
- 204, /* (76) ifexists ::= */
- 160, /* (77) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
- 160, /* (78) cmd ::= DROP VIEW ifexists fullname */
- 160, /* (79) cmd ::= select */
- 174, /* (80) select ::= WITH wqlist selectnowith */
- 174, /* (81) select ::= WITH RECURSIVE wqlist selectnowith */
- 174, /* (82) select ::= selectnowith */
- 206, /* (83) selectnowith ::= selectnowith multiselect_op oneselect */
- 209, /* (84) multiselect_op ::= UNION */
- 209, /* (85) multiselect_op ::= UNION ALL */
- 209, /* (86) multiselect_op ::= EXCEPT|INTERSECT */
- 207, /* (87) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
- 207, /* (88) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
- 219, /* (89) values ::= VALUES LP nexprlist RP */
- 219, /* (90) values ::= values COMMA LP nexprlist RP */
- 210, /* (91) distinct ::= DISTINCT */
- 210, /* (92) distinct ::= ALL */
- 210, /* (93) distinct ::= */
- 221, /* (94) sclp ::= */
- 211, /* (95) selcollist ::= sclp scanpt expr scanpt as */
- 211, /* (96) selcollist ::= sclp scanpt STAR */
- 211, /* (97) selcollist ::= sclp scanpt nm DOT STAR */
- 222, /* (98) as ::= AS nm */
- 222, /* (99) as ::= */
- 212, /* (100) from ::= */
- 212, /* (101) from ::= FROM seltablist */
- 224, /* (102) stl_prefix ::= seltablist joinop */
- 224, /* (103) stl_prefix ::= */
- 223, /* (104) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */
- 223, /* (105) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */
- 223, /* (106) seltablist ::= stl_prefix LP select RP as on_opt using_opt */
- 223, /* (107) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */
- 170, /* (108) dbnm ::= */
- 170, /* (109) dbnm ::= DOT nm */
- 205, /* (110) fullname ::= nm */
- 205, /* (111) fullname ::= nm DOT nm */
- 230, /* (112) xfullname ::= nm */
- 230, /* (113) xfullname ::= nm DOT nm */
- 230, /* (114) xfullname ::= nm DOT nm AS nm */
- 230, /* (115) xfullname ::= nm AS nm */
- 225, /* (116) joinop ::= COMMA|JOIN */
- 225, /* (117) joinop ::= JOIN_KW JOIN */
- 225, /* (118) joinop ::= JOIN_KW nm JOIN */
- 225, /* (119) joinop ::= JOIN_KW nm nm JOIN */
- 227, /* (120) on_opt ::= ON expr */
- 227, /* (121) on_opt ::= */
- 226, /* (122) indexed_opt ::= */
- 226, /* (123) indexed_opt ::= INDEXED BY nm */
- 226, /* (124) indexed_opt ::= NOT INDEXED */
- 228, /* (125) using_opt ::= USING LP idlist RP */
- 228, /* (126) using_opt ::= */
- 216, /* (127) orderby_opt ::= */
- 216, /* (128) orderby_opt ::= ORDER BY sortlist */
- 198, /* (129) sortlist ::= sortlist COMMA expr sortorder */
- 198, /* (130) sortlist ::= expr sortorder */
- 187, /* (131) sortorder ::= ASC */
- 187, /* (132) sortorder ::= DESC */
- 187, /* (133) sortorder ::= */
- 214, /* (134) groupby_opt ::= */
- 214, /* (135) groupby_opt ::= GROUP BY nexprlist */
- 215, /* (136) having_opt ::= */
- 215, /* (137) having_opt ::= HAVING expr */
- 217, /* (138) limit_opt ::= */
- 217, /* (139) limit_opt ::= LIMIT expr */
- 217, /* (140) limit_opt ::= LIMIT expr OFFSET expr */
- 217, /* (141) limit_opt ::= LIMIT expr COMMA expr */
- 160, /* (142) cmd ::= with DELETE FROM xfullname indexed_opt where_opt */
- 213, /* (143) where_opt ::= */
- 213, /* (144) where_opt ::= WHERE expr */
- 160, /* (145) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */
- 233, /* (146) setlist ::= setlist COMMA nm EQ expr */
- 233, /* (147) setlist ::= setlist COMMA LP idlist RP EQ expr */
- 233, /* (148) setlist ::= nm EQ expr */
- 233, /* (149) setlist ::= LP idlist RP EQ expr */
- 160, /* (150) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
- 160, /* (151) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */
- 236, /* (152) upsert ::= */
- 236, /* (153) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */
- 236, /* (154) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */
- 236, /* (155) upsert ::= ON CONFLICT DO NOTHING */
- 234, /* (156) insert_cmd ::= INSERT orconf */
- 234, /* (157) insert_cmd ::= REPLACE */
- 235, /* (158) idlist_opt ::= */
- 235, /* (159) idlist_opt ::= LP idlist RP */
- 231, /* (160) idlist ::= idlist COMMA nm */
- 231, /* (161) idlist ::= nm */
- 185, /* (162) expr ::= LP expr RP */
- 185, /* (163) expr ::= ID|INDEXED */
- 185, /* (164) expr ::= JOIN_KW */
- 185, /* (165) expr ::= nm DOT nm */
- 185, /* (166) expr ::= nm DOT nm DOT nm */
- 184, /* (167) term ::= NULL|FLOAT|BLOB */
- 184, /* (168) term ::= STRING */
- 184, /* (169) term ::= INTEGER */
- 185, /* (170) expr ::= VARIABLE */
- 185, /* (171) expr ::= expr COLLATE ID|STRING */
- 185, /* (172) expr ::= CAST LP expr AS typetoken RP */
- 185, /* (173) expr ::= ID|INDEXED LP distinct exprlist RP */
- 185, /* (174) expr ::= ID|INDEXED LP STAR RP */
- 185, /* (175) expr ::= ID|INDEXED LP distinct exprlist RP over_clause */
- 185, /* (176) expr ::= ID|INDEXED LP STAR RP over_clause */
- 184, /* (177) term ::= CTIME_KW */
- 185, /* (178) expr ::= LP nexprlist COMMA expr RP */
- 185, /* (179) expr ::= expr AND expr */
- 185, /* (180) expr ::= expr OR expr */
- 185, /* (181) expr ::= expr LT|GT|GE|LE expr */
- 185, /* (182) expr ::= expr EQ|NE expr */
- 185, /* (183) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
- 185, /* (184) expr ::= expr PLUS|MINUS expr */
- 185, /* (185) expr ::= expr STAR|SLASH|REM expr */
- 185, /* (186) expr ::= expr CONCAT expr */
- 238, /* (187) likeop ::= NOT LIKE_KW|MATCH */
- 185, /* (188) expr ::= expr likeop expr */
- 185, /* (189) expr ::= expr likeop expr ESCAPE expr */
- 185, /* (190) expr ::= expr ISNULL|NOTNULL */
- 185, /* (191) expr ::= expr NOT NULL */
- 185, /* (192) expr ::= expr IS expr */
- 185, /* (193) expr ::= expr IS NOT expr */
- 185, /* (194) expr ::= NOT expr */
- 185, /* (195) expr ::= BITNOT expr */
- 185, /* (196) expr ::= PLUS|MINUS expr */
- 239, /* (197) between_op ::= BETWEEN */
- 239, /* (198) between_op ::= NOT BETWEEN */
- 185, /* (199) expr ::= expr between_op expr AND expr */
- 240, /* (200) in_op ::= IN */
- 240, /* (201) in_op ::= NOT IN */
- 185, /* (202) expr ::= expr in_op LP exprlist RP */
- 185, /* (203) expr ::= LP select RP */
- 185, /* (204) expr ::= expr in_op LP select RP */
- 185, /* (205) expr ::= expr in_op nm dbnm paren_exprlist */
- 185, /* (206) expr ::= EXISTS LP select RP */
- 185, /* (207) expr ::= CASE case_operand case_exprlist case_else END */
- 243, /* (208) case_exprlist ::= case_exprlist WHEN expr THEN expr */
- 243, /* (209) case_exprlist ::= WHEN expr THEN expr */
- 244, /* (210) case_else ::= ELSE expr */
- 244, /* (211) case_else ::= */
- 242, /* (212) case_operand ::= expr */
- 242, /* (213) case_operand ::= */
- 229, /* (214) exprlist ::= */
- 220, /* (215) nexprlist ::= nexprlist COMMA expr */
- 220, /* (216) nexprlist ::= expr */
- 241, /* (217) paren_exprlist ::= */
- 241, /* (218) paren_exprlist ::= LP exprlist RP */
- 160, /* (219) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
- 245, /* (220) uniqueflag ::= UNIQUE */
- 245, /* (221) uniqueflag ::= */
- 189, /* (222) eidlist_opt ::= */
- 189, /* (223) eidlist_opt ::= LP eidlist RP */
- 199, /* (224) eidlist ::= eidlist COMMA nm collate sortorder */
- 199, /* (225) eidlist ::= nm collate sortorder */
- 246, /* (226) collate ::= */
- 246, /* (227) collate ::= COLLATE ID|STRING */
- 160, /* (228) cmd ::= DROP INDEX ifexists fullname */
- 160, /* (229) cmd ::= VACUUM vinto */
- 160, /* (230) cmd ::= VACUUM nm vinto */
- 247, /* (231) vinto ::= INTO expr */
- 247, /* (232) vinto ::= */
- 160, /* (233) cmd ::= PRAGMA nm dbnm */
- 160, /* (234) cmd ::= PRAGMA nm dbnm EQ nmnum */
- 160, /* (235) cmd ::= PRAGMA nm dbnm LP nmnum RP */
- 160, /* (236) cmd ::= PRAGMA nm dbnm EQ minus_num */
- 160, /* (237) cmd ::= PRAGMA nm dbnm LP minus_num RP */
- 180, /* (238) plus_num ::= PLUS INTEGER|FLOAT */
- 181, /* (239) minus_num ::= MINUS INTEGER|FLOAT */
- 160, /* (240) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
- 249, /* (241) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
- 251, /* (242) trigger_time ::= BEFORE|AFTER */
- 251, /* (243) trigger_time ::= INSTEAD OF */
- 251, /* (244) trigger_time ::= */
- 252, /* (245) trigger_event ::= DELETE|INSERT */
- 252, /* (246) trigger_event ::= UPDATE */
- 252, /* (247) trigger_event ::= UPDATE OF idlist */
- 254, /* (248) when_clause ::= */
- 254, /* (249) when_clause ::= WHEN expr */
- 250, /* (250) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
- 250, /* (251) trigger_cmd_list ::= trigger_cmd SEMI */
- 256, /* (252) trnm ::= nm DOT nm */
- 257, /* (253) tridxby ::= INDEXED BY nm */
- 257, /* (254) tridxby ::= NOT INDEXED */
- 255, /* (255) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */
- 255, /* (256) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
- 255, /* (257) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
- 255, /* (258) trigger_cmd ::= scanpt select scanpt */
- 185, /* (259) expr ::= RAISE LP IGNORE RP */
- 185, /* (260) expr ::= RAISE LP raisetype COMMA nm RP */
- 203, /* (261) raisetype ::= ROLLBACK */
- 203, /* (262) raisetype ::= ABORT */
- 203, /* (263) raisetype ::= FAIL */
- 160, /* (264) cmd ::= DROP TRIGGER ifexists fullname */
- 160, /* (265) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
- 160, /* (266) cmd ::= DETACH database_kw_opt expr */
- 259, /* (267) key_opt ::= */
- 259, /* (268) key_opt ::= KEY expr */
- 160, /* (269) cmd ::= REINDEX */
- 160, /* (270) cmd ::= REINDEX nm dbnm */
- 160, /* (271) cmd ::= ANALYZE */
- 160, /* (272) cmd ::= ANALYZE nm dbnm */
- 160, /* (273) cmd ::= ALTER TABLE fullname RENAME TO nm */
- 160, /* (274) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
- 260, /* (275) add_column_fullname ::= fullname */
- 160, /* (276) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
- 160, /* (277) cmd ::= create_vtab */
- 160, /* (278) cmd ::= create_vtab LP vtabarglist RP */
- 262, /* (279) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
- 264, /* (280) vtabarg ::= */
- 265, /* (281) vtabargtoken ::= ANY */
- 265, /* (282) vtabargtoken ::= lp anylist RP */
- 266, /* (283) lp ::= LP */
- 232, /* (284) with ::= WITH wqlist */
- 232, /* (285) with ::= WITH RECURSIVE wqlist */
- 208, /* (286) wqlist ::= nm eidlist_opt AS LP select RP */
- 208, /* (287) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
- 268, /* (288) windowdefn_list ::= windowdefn */
- 268, /* (289) windowdefn_list ::= windowdefn_list COMMA windowdefn */
- 269, /* (290) windowdefn ::= nm AS window */
- 270, /* (291) window ::= LP part_opt orderby_opt frame_opt RP */
- 272, /* (292) part_opt ::= PARTITION BY nexprlist */
- 272, /* (293) part_opt ::= */
- 271, /* (294) frame_opt ::= */
- 271, /* (295) frame_opt ::= range_or_rows frame_bound_s */
- 271, /* (296) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e */
- 274, /* (297) range_or_rows ::= RANGE */
- 274, /* (298) range_or_rows ::= ROWS */
- 276, /* (299) frame_bound_s ::= frame_bound */
- 276, /* (300) frame_bound_s ::= UNBOUNDED PRECEDING */
- 277, /* (301) frame_bound_e ::= frame_bound */
- 277, /* (302) frame_bound_e ::= UNBOUNDED FOLLOWING */
- 275, /* (303) frame_bound ::= expr PRECEDING */
- 275, /* (304) frame_bound ::= CURRENT ROW */
- 275, /* (305) frame_bound ::= expr FOLLOWING */
- 218, /* (306) window_clause ::= WINDOW windowdefn_list */
- 237, /* (307) over_clause ::= filter_opt OVER window */
- 237, /* (308) over_clause ::= filter_opt OVER nm */
- 273, /* (309) filter_opt ::= */
- 273, /* (310) filter_opt ::= FILTER LP WHERE expr RP */
- 155, /* (311) input ::= cmdlist */
- 156, /* (312) cmdlist ::= cmdlist ecmd */
- 156, /* (313) cmdlist ::= ecmd */
- 157, /* (314) ecmd ::= SEMI */
- 157, /* (315) ecmd ::= cmdx SEMI */
- 157, /* (316) ecmd ::= explain cmdx */
- 162, /* (317) trans_opt ::= */
- 162, /* (318) trans_opt ::= TRANSACTION */
- 162, /* (319) trans_opt ::= TRANSACTION nm */
- 164, /* (320) savepoint_opt ::= SAVEPOINT */
- 164, /* (321) savepoint_opt ::= */
- 160, /* (322) cmd ::= create_table create_table_args */
- 171, /* (323) columnlist ::= columnlist COMMA columnname carglist */
- 171, /* (324) columnlist ::= columnname carglist */
- 163, /* (325) nm ::= ID|INDEXED */
- 163, /* (326) nm ::= STRING */
- 163, /* (327) nm ::= JOIN_KW */
- 177, /* (328) typetoken ::= typename */
- 178, /* (329) typename ::= ID|STRING */
- 179, /* (330) signed ::= plus_num */
- 179, /* (331) signed ::= minus_num */
- 176, /* (332) carglist ::= carglist ccons */
- 176, /* (333) carglist ::= */
- 183, /* (334) ccons ::= NULL onconf */
- 172, /* (335) conslist_opt ::= COMMA conslist */
- 195, /* (336) conslist ::= conslist tconscomma tcons */
- 195, /* (337) conslist ::= tcons */
- 196, /* (338) tconscomma ::= */
- 200, /* (339) defer_subclause_opt ::= defer_subclause */
- 202, /* (340) resolvetype ::= raisetype */
- 206, /* (341) selectnowith ::= oneselect */
- 207, /* (342) oneselect ::= values */
- 221, /* (343) sclp ::= selcollist COMMA */
- 222, /* (344) as ::= ID|STRING */
- 185, /* (345) expr ::= term */
- 238, /* (346) likeop ::= LIKE_KW|MATCH */
- 229, /* (347) exprlist ::= nexprlist */
- 248, /* (348) nmnum ::= plus_num */
- 248, /* (349) nmnum ::= nm */
- 248, /* (350) nmnum ::= ON */
- 248, /* (351) nmnum ::= DELETE */
- 248, /* (352) nmnum ::= DEFAULT */
- 180, /* (353) plus_num ::= INTEGER|FLOAT */
- 253, /* (354) foreach_clause ::= */
- 253, /* (355) foreach_clause ::= FOR EACH ROW */
- 256, /* (356) trnm ::= nm */
- 257, /* (357) tridxby ::= */
- 258, /* (358) database_kw_opt ::= DATABASE */
- 258, /* (359) database_kw_opt ::= */
- 261, /* (360) kwcolumn_opt ::= */
- 261, /* (361) kwcolumn_opt ::= COLUMNKW */
- 263, /* (362) vtabarglist ::= vtabarg */
- 263, /* (363) vtabarglist ::= vtabarglist COMMA vtabarg */
- 264, /* (364) vtabarg ::= vtabarg vtabargtoken */
- 267, /* (365) anylist ::= */
- 267, /* (366) anylist ::= anylist LP anylist RP */
- 267, /* (367) anylist ::= anylist ANY */
- 232, /* (368) with ::= */
+ 180, /* (0) explain ::= EXPLAIN */
+ 180, /* (1) explain ::= EXPLAIN QUERY PLAN */
+ 179, /* (2) cmdx ::= cmd */
+ 181, /* (3) cmd ::= BEGIN transtype trans_opt */
+ 182, /* (4) transtype ::= */
+ 182, /* (5) transtype ::= DEFERRED */
+ 182, /* (6) transtype ::= IMMEDIATE */
+ 182, /* (7) transtype ::= EXCLUSIVE */
+ 181, /* (8) cmd ::= COMMIT|END trans_opt */
+ 181, /* (9) cmd ::= ROLLBACK trans_opt */
+ 181, /* (10) cmd ::= SAVEPOINT nm */
+ 181, /* (11) cmd ::= RELEASE savepoint_opt nm */
+ 181, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */
+ 186, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */
+ 188, /* (14) createkw ::= CREATE */
+ 190, /* (15) ifnotexists ::= */
+ 190, /* (16) ifnotexists ::= IF NOT EXISTS */
+ 189, /* (17) temp ::= TEMP */
+ 189, /* (18) temp ::= */
+ 187, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_options */
+ 187, /* (20) create_table_args ::= AS select */
+ 194, /* (21) table_options ::= */
+ 194, /* (22) table_options ::= WITHOUT nm */
+ 196, /* (23) columnname ::= nm typetoken */
+ 198, /* (24) typetoken ::= */
+ 198, /* (25) typetoken ::= typename LP signed RP */
+ 198, /* (26) typetoken ::= typename LP signed COMMA signed RP */
+ 199, /* (27) typename ::= typename ID|STRING */
+ 203, /* (28) scanpt ::= */
+ 204, /* (29) ccons ::= CONSTRAINT nm */
+ 204, /* (30) ccons ::= DEFAULT scanpt term scanpt */
+ 204, /* (31) ccons ::= DEFAULT LP expr RP */
+ 204, /* (32) ccons ::= DEFAULT PLUS term scanpt */
+ 204, /* (33) ccons ::= DEFAULT MINUS term scanpt */
+ 204, /* (34) ccons ::= DEFAULT scanpt ID|INDEXED */
+ 204, /* (35) ccons ::= NOT NULL onconf */
+ 204, /* (36) ccons ::= PRIMARY KEY sortorder onconf autoinc */
+ 204, /* (37) ccons ::= UNIQUE onconf */
+ 204, /* (38) ccons ::= CHECK LP expr RP */
+ 204, /* (39) ccons ::= REFERENCES nm eidlist_opt refargs */
+ 204, /* (40) ccons ::= defer_subclause */
+ 204, /* (41) ccons ::= COLLATE ID|STRING */
+ 209, /* (42) autoinc ::= */
+ 209, /* (43) autoinc ::= AUTOINCR */
+ 211, /* (44) refargs ::= */
+ 211, /* (45) refargs ::= refargs refarg */
+ 213, /* (46) refarg ::= MATCH nm */
+ 213, /* (47) refarg ::= ON INSERT refact */
+ 213, /* (48) refarg ::= ON DELETE refact */
+ 213, /* (49) refarg ::= ON UPDATE refact */
+ 214, /* (50) refact ::= SET NULL */
+ 214, /* (51) refact ::= SET DEFAULT */
+ 214, /* (52) refact ::= CASCADE */
+ 214, /* (53) refact ::= RESTRICT */
+ 214, /* (54) refact ::= NO ACTION */
+ 212, /* (55) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
+ 212, /* (56) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
+ 215, /* (57) init_deferred_pred_opt ::= */
+ 215, /* (58) init_deferred_pred_opt ::= INITIALLY DEFERRED */
+ 215, /* (59) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
+ 193, /* (60) conslist_opt ::= */
+ 217, /* (61) tconscomma ::= COMMA */
+ 218, /* (62) tcons ::= CONSTRAINT nm */
+ 218, /* (63) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
+ 218, /* (64) tcons ::= UNIQUE LP sortlist RP onconf */
+ 218, /* (65) tcons ::= CHECK LP expr RP onconf */
+ 218, /* (66) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
+ 221, /* (67) defer_subclause_opt ::= */
+ 207, /* (68) onconf ::= */
+ 207, /* (69) onconf ::= ON CONFLICT resolvetype */
+ 222, /* (70) orconf ::= */
+ 222, /* (71) orconf ::= OR resolvetype */
+ 223, /* (72) resolvetype ::= IGNORE */
+ 223, /* (73) resolvetype ::= REPLACE */
+ 181, /* (74) cmd ::= DROP TABLE ifexists fullname */
+ 225, /* (75) ifexists ::= IF EXISTS */
+ 225, /* (76) ifexists ::= */
+ 181, /* (77) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
+ 181, /* (78) cmd ::= DROP VIEW ifexists fullname */
+ 181, /* (79) cmd ::= select */
+ 195, /* (80) select ::= WITH wqlist selectnowith */
+ 195, /* (81) select ::= WITH RECURSIVE wqlist selectnowith */
+ 195, /* (82) select ::= selectnowith */
+ 227, /* (83) selectnowith ::= selectnowith multiselect_op oneselect */
+ 230, /* (84) multiselect_op ::= UNION */
+ 230, /* (85) multiselect_op ::= UNION ALL */
+ 230, /* (86) multiselect_op ::= EXCEPT|INTERSECT */
+ 228, /* (87) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
+ 228, /* (88) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
+ 240, /* (89) values ::= VALUES LP nexprlist RP */
+ 240, /* (90) values ::= values COMMA LP nexprlist RP */
+ 231, /* (91) distinct ::= DISTINCT */
+ 231, /* (92) distinct ::= ALL */
+ 231, /* (93) distinct ::= */
+ 242, /* (94) sclp ::= */
+ 232, /* (95) selcollist ::= sclp scanpt expr scanpt as */
+ 232, /* (96) selcollist ::= sclp scanpt STAR */
+ 232, /* (97) selcollist ::= sclp scanpt nm DOT STAR */
+ 243, /* (98) as ::= AS nm */
+ 243, /* (99) as ::= */
+ 233, /* (100) from ::= */
+ 233, /* (101) from ::= FROM seltablist */
+ 245, /* (102) stl_prefix ::= seltablist joinop */
+ 245, /* (103) stl_prefix ::= */
+ 244, /* (104) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */
+ 244, /* (105) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */
+ 244, /* (106) seltablist ::= stl_prefix LP select RP as on_opt using_opt */
+ 244, /* (107) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */
+ 191, /* (108) dbnm ::= */
+ 191, /* (109) dbnm ::= DOT nm */
+ 226, /* (110) fullname ::= nm */
+ 226, /* (111) fullname ::= nm DOT nm */
+ 251, /* (112) xfullname ::= nm */
+ 251, /* (113) xfullname ::= nm DOT nm */
+ 251, /* (114) xfullname ::= nm DOT nm AS nm */
+ 251, /* (115) xfullname ::= nm AS nm */
+ 246, /* (116) joinop ::= COMMA|JOIN */
+ 246, /* (117) joinop ::= JOIN_KW JOIN */
+ 246, /* (118) joinop ::= JOIN_KW nm JOIN */
+ 246, /* (119) joinop ::= JOIN_KW nm nm JOIN */
+ 248, /* (120) on_opt ::= ON expr */
+ 248, /* (121) on_opt ::= */
+ 247, /* (122) indexed_opt ::= */
+ 247, /* (123) indexed_opt ::= INDEXED BY nm */
+ 247, /* (124) indexed_opt ::= NOT INDEXED */
+ 249, /* (125) using_opt ::= USING LP idlist RP */
+ 249, /* (126) using_opt ::= */
+ 237, /* (127) orderby_opt ::= */
+ 237, /* (128) orderby_opt ::= ORDER BY sortlist */
+ 219, /* (129) sortlist ::= sortlist COMMA expr sortorder */
+ 219, /* (130) sortlist ::= expr sortorder */
+ 208, /* (131) sortorder ::= ASC */
+ 208, /* (132) sortorder ::= DESC */
+ 208, /* (133) sortorder ::= */
+ 235, /* (134) groupby_opt ::= */
+ 235, /* (135) groupby_opt ::= GROUP BY nexprlist */
+ 236, /* (136) having_opt ::= */
+ 236, /* (137) having_opt ::= HAVING expr */
+ 238, /* (138) limit_opt ::= */
+ 238, /* (139) limit_opt ::= LIMIT expr */
+ 238, /* (140) limit_opt ::= LIMIT expr OFFSET expr */
+ 238, /* (141) limit_opt ::= LIMIT expr COMMA expr */
+ 181, /* (142) cmd ::= with DELETE FROM xfullname indexed_opt where_opt */
+ 234, /* (143) where_opt ::= */
+ 234, /* (144) where_opt ::= WHERE expr */
+ 181, /* (145) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */
+ 254, /* (146) setlist ::= setlist COMMA nm EQ expr */
+ 254, /* (147) setlist ::= setlist COMMA LP idlist RP EQ expr */
+ 254, /* (148) setlist ::= nm EQ expr */
+ 254, /* (149) setlist ::= LP idlist RP EQ expr */
+ 181, /* (150) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
+ 181, /* (151) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */
+ 257, /* (152) upsert ::= */
+ 257, /* (153) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */
+ 257, /* (154) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */
+ 257, /* (155) upsert ::= ON CONFLICT DO NOTHING */
+ 255, /* (156) insert_cmd ::= INSERT orconf */
+ 255, /* (157) insert_cmd ::= REPLACE */
+ 256, /* (158) idlist_opt ::= */
+ 256, /* (159) idlist_opt ::= LP idlist RP */
+ 252, /* (160) idlist ::= idlist COMMA nm */
+ 252, /* (161) idlist ::= nm */
+ 206, /* (162) expr ::= LP expr RP */
+ 206, /* (163) expr ::= ID|INDEXED */
+ 206, /* (164) expr ::= JOIN_KW */
+ 206, /* (165) expr ::= nm DOT nm */
+ 206, /* (166) expr ::= nm DOT nm DOT nm */
+ 205, /* (167) term ::= NULL|FLOAT|BLOB */
+ 205, /* (168) term ::= STRING */
+ 205, /* (169) term ::= INTEGER */
+ 206, /* (170) expr ::= VARIABLE */
+ 206, /* (171) expr ::= expr COLLATE ID|STRING */
+ 206, /* (172) expr ::= CAST LP expr AS typetoken RP */
+ 206, /* (173) expr ::= ID|INDEXED LP distinct exprlist RP */
+ 206, /* (174) expr ::= ID|INDEXED LP STAR RP */
+ 206, /* (175) expr ::= ID|INDEXED LP distinct exprlist RP over_clause */
+ 206, /* (176) expr ::= ID|INDEXED LP STAR RP over_clause */
+ 205, /* (177) term ::= CTIME_KW */
+ 206, /* (178) expr ::= LP nexprlist COMMA expr RP */
+ 206, /* (179) expr ::= expr AND expr */
+ 206, /* (180) expr ::= expr OR expr */
+ 206, /* (181) expr ::= expr LT|GT|GE|LE expr */
+ 206, /* (182) expr ::= expr EQ|NE expr */
+ 206, /* (183) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
+ 206, /* (184) expr ::= expr PLUS|MINUS expr */
+ 206, /* (185) expr ::= expr STAR|SLASH|REM expr */
+ 206, /* (186) expr ::= expr CONCAT expr */
+ 259, /* (187) likeop ::= NOT LIKE_KW|MATCH */
+ 206, /* (188) expr ::= expr likeop expr */
+ 206, /* (189) expr ::= expr likeop expr ESCAPE expr */
+ 206, /* (190) expr ::= expr ISNULL|NOTNULL */
+ 206, /* (191) expr ::= expr NOT NULL */
+ 206, /* (192) expr ::= expr IS expr */
+ 206, /* (193) expr ::= expr IS NOT expr */
+ 206, /* (194) expr ::= NOT expr */
+ 206, /* (195) expr ::= BITNOT expr */
+ 206, /* (196) expr ::= PLUS|MINUS expr */
+ 260, /* (197) between_op ::= BETWEEN */
+ 260, /* (198) between_op ::= NOT BETWEEN */
+ 206, /* (199) expr ::= expr between_op expr AND expr */
+ 261, /* (200) in_op ::= IN */
+ 261, /* (201) in_op ::= NOT IN */
+ 206, /* (202) expr ::= expr in_op LP exprlist RP */
+ 206, /* (203) expr ::= LP select RP */
+ 206, /* (204) expr ::= expr in_op LP select RP */
+ 206, /* (205) expr ::= expr in_op nm dbnm paren_exprlist */
+ 206, /* (206) expr ::= EXISTS LP select RP */
+ 206, /* (207) expr ::= CASE case_operand case_exprlist case_else END */
+ 264, /* (208) case_exprlist ::= case_exprlist WHEN expr THEN expr */
+ 264, /* (209) case_exprlist ::= WHEN expr THEN expr */
+ 265, /* (210) case_else ::= ELSE expr */
+ 265, /* (211) case_else ::= */
+ 263, /* (212) case_operand ::= expr */
+ 263, /* (213) case_operand ::= */
+ 250, /* (214) exprlist ::= */
+ 241, /* (215) nexprlist ::= nexprlist COMMA expr */
+ 241, /* (216) nexprlist ::= expr */
+ 262, /* (217) paren_exprlist ::= */
+ 262, /* (218) paren_exprlist ::= LP exprlist RP */
+ 181, /* (219) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
+ 266, /* (220) uniqueflag ::= UNIQUE */
+ 266, /* (221) uniqueflag ::= */
+ 210, /* (222) eidlist_opt ::= */
+ 210, /* (223) eidlist_opt ::= LP eidlist RP */
+ 220, /* (224) eidlist ::= eidlist COMMA nm collate sortorder */
+ 220, /* (225) eidlist ::= nm collate sortorder */
+ 267, /* (226) collate ::= */
+ 267, /* (227) collate ::= COLLATE ID|STRING */
+ 181, /* (228) cmd ::= DROP INDEX ifexists fullname */
+ 181, /* (229) cmd ::= VACUUM vinto */
+ 181, /* (230) cmd ::= VACUUM nm vinto */
+ 268, /* (231) vinto ::= INTO expr */
+ 268, /* (232) vinto ::= */
+ 181, /* (233) cmd ::= PRAGMA nm dbnm */
+ 181, /* (234) cmd ::= PRAGMA nm dbnm EQ nmnum */
+ 181, /* (235) cmd ::= PRAGMA nm dbnm LP nmnum RP */
+ 181, /* (236) cmd ::= PRAGMA nm dbnm EQ minus_num */
+ 181, /* (237) cmd ::= PRAGMA nm dbnm LP minus_num RP */
+ 201, /* (238) plus_num ::= PLUS INTEGER|FLOAT */
+ 202, /* (239) minus_num ::= MINUS INTEGER|FLOAT */
+ 181, /* (240) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
+ 270, /* (241) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
+ 272, /* (242) trigger_time ::= BEFORE|AFTER */
+ 272, /* (243) trigger_time ::= INSTEAD OF */
+ 272, /* (244) trigger_time ::= */
+ 273, /* (245) trigger_event ::= DELETE|INSERT */
+ 273, /* (246) trigger_event ::= UPDATE */
+ 273, /* (247) trigger_event ::= UPDATE OF idlist */
+ 275, /* (248) when_clause ::= */
+ 275, /* (249) when_clause ::= WHEN expr */
+ 271, /* (250) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
+ 271, /* (251) trigger_cmd_list ::= trigger_cmd SEMI */
+ 277, /* (252) trnm ::= nm DOT nm */
+ 278, /* (253) tridxby ::= INDEXED BY nm */
+ 278, /* (254) tridxby ::= NOT INDEXED */
+ 276, /* (255) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */
+ 276, /* (256) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
+ 276, /* (257) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
+ 276, /* (258) trigger_cmd ::= scanpt select scanpt */
+ 206, /* (259) expr ::= RAISE LP IGNORE RP */
+ 206, /* (260) expr ::= RAISE LP raisetype COMMA nm RP */
+ 224, /* (261) raisetype ::= ROLLBACK */
+ 224, /* (262) raisetype ::= ABORT */
+ 224, /* (263) raisetype ::= FAIL */
+ 181, /* (264) cmd ::= DROP TRIGGER ifexists fullname */
+ 181, /* (265) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
+ 181, /* (266) cmd ::= DETACH database_kw_opt expr */
+ 280, /* (267) key_opt ::= */
+ 280, /* (268) key_opt ::= KEY expr */
+ 181, /* (269) cmd ::= REINDEX */
+ 181, /* (270) cmd ::= REINDEX nm dbnm */
+ 181, /* (271) cmd ::= ANALYZE */
+ 181, /* (272) cmd ::= ANALYZE nm dbnm */
+ 181, /* (273) cmd ::= ALTER TABLE fullname RENAME TO nm */
+ 181, /* (274) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
+ 281, /* (275) add_column_fullname ::= fullname */
+ 181, /* (276) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
+ 181, /* (277) cmd ::= create_vtab */
+ 181, /* (278) cmd ::= create_vtab LP vtabarglist RP */
+ 283, /* (279) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
+ 285, /* (280) vtabarg ::= */
+ 286, /* (281) vtabargtoken ::= ANY */
+ 286, /* (282) vtabargtoken ::= lp anylist RP */
+ 287, /* (283) lp ::= LP */
+ 253, /* (284) with ::= WITH wqlist */
+ 253, /* (285) with ::= WITH RECURSIVE wqlist */
+ 229, /* (286) wqlist ::= nm eidlist_opt AS LP select RP */
+ 229, /* (287) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
+ 289, /* (288) windowdefn_list ::= windowdefn */
+ 289, /* (289) windowdefn_list ::= windowdefn_list COMMA windowdefn */
+ 290, /* (290) windowdefn ::= nm AS LP window RP */
+ 291, /* (291) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
+ 291, /* (292) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
+ 291, /* (293) window ::= ORDER BY sortlist frame_opt */
+ 291, /* (294) window ::= nm ORDER BY sortlist frame_opt */
+ 291, /* (295) window ::= frame_opt */
+ 291, /* (296) window ::= nm frame_opt */
+ 292, /* (297) frame_opt ::= */
+ 292, /* (298) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
+ 292, /* (299) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
+ 295, /* (300) range_or_rows ::= RANGE|ROWS|GROUPS */
+ 297, /* (301) frame_bound_s ::= frame_bound */
+ 297, /* (302) frame_bound_s ::= UNBOUNDED PRECEDING */
+ 298, /* (303) frame_bound_e ::= frame_bound */
+ 298, /* (304) frame_bound_e ::= UNBOUNDED FOLLOWING */
+ 296, /* (305) frame_bound ::= expr PRECEDING|FOLLOWING */
+ 296, /* (306) frame_bound ::= CURRENT ROW */
+ 299, /* (307) frame_exclude_opt ::= */
+ 299, /* (308) frame_exclude_opt ::= EXCLUDE frame_exclude */
+ 300, /* (309) frame_exclude ::= NO OTHERS */
+ 300, /* (310) frame_exclude ::= CURRENT ROW */
+ 300, /* (311) frame_exclude ::= GROUP|TIES */
+ 239, /* (312) window_clause ::= WINDOW windowdefn_list */
+ 258, /* (313) over_clause ::= filter_opt OVER LP window RP */
+ 258, /* (314) over_clause ::= filter_opt OVER nm */
+ 294, /* (315) filter_opt ::= */
+ 294, /* (316) filter_opt ::= FILTER LP WHERE expr RP */
+ 176, /* (317) input ::= cmdlist */
+ 177, /* (318) cmdlist ::= cmdlist ecmd */
+ 177, /* (319) cmdlist ::= ecmd */
+ 178, /* (320) ecmd ::= SEMI */
+ 178, /* (321) ecmd ::= cmdx SEMI */
+ 178, /* (322) ecmd ::= explain cmdx */
+ 183, /* (323) trans_opt ::= */
+ 183, /* (324) trans_opt ::= TRANSACTION */
+ 183, /* (325) trans_opt ::= TRANSACTION nm */
+ 185, /* (326) savepoint_opt ::= SAVEPOINT */
+ 185, /* (327) savepoint_opt ::= */
+ 181, /* (328) cmd ::= create_table create_table_args */
+ 192, /* (329) columnlist ::= columnlist COMMA columnname carglist */
+ 192, /* (330) columnlist ::= columnname carglist */
+ 184, /* (331) nm ::= ID|INDEXED */
+ 184, /* (332) nm ::= STRING */
+ 184, /* (333) nm ::= JOIN_KW */
+ 198, /* (334) typetoken ::= typename */
+ 199, /* (335) typename ::= ID|STRING */
+ 200, /* (336) signed ::= plus_num */
+ 200, /* (337) signed ::= minus_num */
+ 197, /* (338) carglist ::= carglist ccons */
+ 197, /* (339) carglist ::= */
+ 204, /* (340) ccons ::= NULL onconf */
+ 193, /* (341) conslist_opt ::= COMMA conslist */
+ 216, /* (342) conslist ::= conslist tconscomma tcons */
+ 216, /* (343) conslist ::= tcons */
+ 217, /* (344) tconscomma ::= */
+ 221, /* (345) defer_subclause_opt ::= defer_subclause */
+ 223, /* (346) resolvetype ::= raisetype */
+ 227, /* (347) selectnowith ::= oneselect */
+ 228, /* (348) oneselect ::= values */
+ 242, /* (349) sclp ::= selcollist COMMA */
+ 243, /* (350) as ::= ID|STRING */
+ 206, /* (351) expr ::= term */
+ 259, /* (352) likeop ::= LIKE_KW|MATCH */
+ 250, /* (353) exprlist ::= nexprlist */
+ 269, /* (354) nmnum ::= plus_num */
+ 269, /* (355) nmnum ::= nm */
+ 269, /* (356) nmnum ::= ON */
+ 269, /* (357) nmnum ::= DELETE */
+ 269, /* (358) nmnum ::= DEFAULT */
+ 201, /* (359) plus_num ::= INTEGER|FLOAT */
+ 274, /* (360) foreach_clause ::= */
+ 274, /* (361) foreach_clause ::= FOR EACH ROW */
+ 277, /* (362) trnm ::= nm */
+ 278, /* (363) tridxby ::= */
+ 279, /* (364) database_kw_opt ::= DATABASE */
+ 279, /* (365) database_kw_opt ::= */
+ 282, /* (366) kwcolumn_opt ::= */
+ 282, /* (367) kwcolumn_opt ::= COLUMNKW */
+ 284, /* (368) vtabarglist ::= vtabarg */
+ 284, /* (369) vtabarglist ::= vtabarglist COMMA vtabarg */
+ 285, /* (370) vtabarg ::= vtabarg vtabargtoken */
+ 288, /* (371) anylist ::= */
+ 288, /* (372) anylist ::= anylist LP anylist RP */
+ 288, /* (373) anylist ::= anylist ANY */
+ 253, /* (374) with ::= */
};
/* For rule J, yyRuleInfoNRhs[J] contains the negative of the number
@@ -150264,85 +151140,91 @@ static const signed char yyRuleInfoNRhs[] = {
-8, /* (287) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
-1, /* (288) windowdefn_list ::= windowdefn */
-3, /* (289) windowdefn_list ::= windowdefn_list COMMA windowdefn */
- -3, /* (290) windowdefn ::= nm AS window */
- -5, /* (291) window ::= LP part_opt orderby_opt frame_opt RP */
- -3, /* (292) part_opt ::= PARTITION BY nexprlist */
- 0, /* (293) part_opt ::= */
- 0, /* (294) frame_opt ::= */
- -2, /* (295) frame_opt ::= range_or_rows frame_bound_s */
- -5, /* (296) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e */
- -1, /* (297) range_or_rows ::= RANGE */
- -1, /* (298) range_or_rows ::= ROWS */
- -1, /* (299) frame_bound_s ::= frame_bound */
- -2, /* (300) frame_bound_s ::= UNBOUNDED PRECEDING */
- -1, /* (301) frame_bound_e ::= frame_bound */
- -2, /* (302) frame_bound_e ::= UNBOUNDED FOLLOWING */
- -2, /* (303) frame_bound ::= expr PRECEDING */
- -2, /* (304) frame_bound ::= CURRENT ROW */
- -2, /* (305) frame_bound ::= expr FOLLOWING */
- -2, /* (306) window_clause ::= WINDOW windowdefn_list */
- -3, /* (307) over_clause ::= filter_opt OVER window */
- -3, /* (308) over_clause ::= filter_opt OVER nm */
- 0, /* (309) filter_opt ::= */
- -5, /* (310) filter_opt ::= FILTER LP WHERE expr RP */
- -1, /* (311) input ::= cmdlist */
- -2, /* (312) cmdlist ::= cmdlist ecmd */
- -1, /* (313) cmdlist ::= ecmd */
- -1, /* (314) ecmd ::= SEMI */
- -2, /* (315) ecmd ::= cmdx SEMI */
- -2, /* (316) ecmd ::= explain cmdx */
- 0, /* (317) trans_opt ::= */
- -1, /* (318) trans_opt ::= TRANSACTION */
- -2, /* (319) trans_opt ::= TRANSACTION nm */
- -1, /* (320) savepoint_opt ::= SAVEPOINT */
- 0, /* (321) savepoint_opt ::= */
- -2, /* (322) cmd ::= create_table create_table_args */
- -4, /* (323) columnlist ::= columnlist COMMA columnname carglist */
- -2, /* (324) columnlist ::= columnname carglist */
- -1, /* (325) nm ::= ID|INDEXED */
- -1, /* (326) nm ::= STRING */
- -1, /* (327) nm ::= JOIN_KW */
- -1, /* (328) typetoken ::= typename */
- -1, /* (329) typename ::= ID|STRING */
- -1, /* (330) signed ::= plus_num */
- -1, /* (331) signed ::= minus_num */
- -2, /* (332) carglist ::= carglist ccons */
- 0, /* (333) carglist ::= */
- -2, /* (334) ccons ::= NULL onconf */
- -2, /* (335) conslist_opt ::= COMMA conslist */
- -3, /* (336) conslist ::= conslist tconscomma tcons */
- -1, /* (337) conslist ::= tcons */
- 0, /* (338) tconscomma ::= */
- -1, /* (339) defer_subclause_opt ::= defer_subclause */
- -1, /* (340) resolvetype ::= raisetype */
- -1, /* (341) selectnowith ::= oneselect */
- -1, /* (342) oneselect ::= values */
- -2, /* (343) sclp ::= selcollist COMMA */
- -1, /* (344) as ::= ID|STRING */
- -1, /* (345) expr ::= term */
- -1, /* (346) likeop ::= LIKE_KW|MATCH */
- -1, /* (347) exprlist ::= nexprlist */
- -1, /* (348) nmnum ::= plus_num */
- -1, /* (349) nmnum ::= nm */
- -1, /* (350) nmnum ::= ON */
- -1, /* (351) nmnum ::= DELETE */
- -1, /* (352) nmnum ::= DEFAULT */
- -1, /* (353) plus_num ::= INTEGER|FLOAT */
- 0, /* (354) foreach_clause ::= */
- -3, /* (355) foreach_clause ::= FOR EACH ROW */
- -1, /* (356) trnm ::= nm */
- 0, /* (357) tridxby ::= */
- -1, /* (358) database_kw_opt ::= DATABASE */
- 0, /* (359) database_kw_opt ::= */
- 0, /* (360) kwcolumn_opt ::= */
- -1, /* (361) kwcolumn_opt ::= COLUMNKW */
- -1, /* (362) vtabarglist ::= vtabarg */
- -3, /* (363) vtabarglist ::= vtabarglist COMMA vtabarg */
- -2, /* (364) vtabarg ::= vtabarg vtabargtoken */
- 0, /* (365) anylist ::= */
- -4, /* (366) anylist ::= anylist LP anylist RP */
- -2, /* (367) anylist ::= anylist ANY */
- 0, /* (368) with ::= */
+ -5, /* (290) windowdefn ::= nm AS LP window RP */
+ -5, /* (291) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
+ -6, /* (292) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
+ -4, /* (293) window ::= ORDER BY sortlist frame_opt */
+ -5, /* (294) window ::= nm ORDER BY sortlist frame_opt */
+ -1, /* (295) window ::= frame_opt */
+ -2, /* (296) window ::= nm frame_opt */
+ 0, /* (297) frame_opt ::= */
+ -3, /* (298) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
+ -6, /* (299) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
+ -1, /* (300) range_or_rows ::= RANGE|ROWS|GROUPS */
+ -1, /* (301) frame_bound_s ::= frame_bound */
+ -2, /* (302) frame_bound_s ::= UNBOUNDED PRECEDING */
+ -1, /* (303) frame_bound_e ::= frame_bound */
+ -2, /* (304) frame_bound_e ::= UNBOUNDED FOLLOWING */
+ -2, /* (305) frame_bound ::= expr PRECEDING|FOLLOWING */
+ -2, /* (306) frame_bound ::= CURRENT ROW */
+ 0, /* (307) frame_exclude_opt ::= */
+ -2, /* (308) frame_exclude_opt ::= EXCLUDE frame_exclude */
+ -2, /* (309) frame_exclude ::= NO OTHERS */
+ -2, /* (310) frame_exclude ::= CURRENT ROW */
+ -1, /* (311) frame_exclude ::= GROUP|TIES */
+ -2, /* (312) window_clause ::= WINDOW windowdefn_list */
+ -5, /* (313) over_clause ::= filter_opt OVER LP window RP */
+ -3, /* (314) over_clause ::= filter_opt OVER nm */
+ 0, /* (315) filter_opt ::= */
+ -5, /* (316) filter_opt ::= FILTER LP WHERE expr RP */
+ -1, /* (317) input ::= cmdlist */
+ -2, /* (318) cmdlist ::= cmdlist ecmd */
+ -1, /* (319) cmdlist ::= ecmd */
+ -1, /* (320) ecmd ::= SEMI */
+ -2, /* (321) ecmd ::= cmdx SEMI */
+ -2, /* (322) ecmd ::= explain cmdx */
+ 0, /* (323) trans_opt ::= */
+ -1, /* (324) trans_opt ::= TRANSACTION */
+ -2, /* (325) trans_opt ::= TRANSACTION nm */
+ -1, /* (326) savepoint_opt ::= SAVEPOINT */
+ 0, /* (327) savepoint_opt ::= */
+ -2, /* (328) cmd ::= create_table create_table_args */
+ -4, /* (329) columnlist ::= columnlist COMMA columnname carglist */
+ -2, /* (330) columnlist ::= columnname carglist */
+ -1, /* (331) nm ::= ID|INDEXED */
+ -1, /* (332) nm ::= STRING */
+ -1, /* (333) nm ::= JOIN_KW */
+ -1, /* (334) typetoken ::= typename */
+ -1, /* (335) typename ::= ID|STRING */
+ -1, /* (336) signed ::= plus_num */
+ -1, /* (337) signed ::= minus_num */
+ -2, /* (338) carglist ::= carglist ccons */
+ 0, /* (339) carglist ::= */
+ -2, /* (340) ccons ::= NULL onconf */
+ -2, /* (341) conslist_opt ::= COMMA conslist */
+ -3, /* (342) conslist ::= conslist tconscomma tcons */
+ -1, /* (343) conslist ::= tcons */
+ 0, /* (344) tconscomma ::= */
+ -1, /* (345) defer_subclause_opt ::= defer_subclause */
+ -1, /* (346) resolvetype ::= raisetype */
+ -1, /* (347) selectnowith ::= oneselect */
+ -1, /* (348) oneselect ::= values */
+ -2, /* (349) sclp ::= selcollist COMMA */
+ -1, /* (350) as ::= ID|STRING */
+ -1, /* (351) expr ::= term */
+ -1, /* (352) likeop ::= LIKE_KW|MATCH */
+ -1, /* (353) exprlist ::= nexprlist */
+ -1, /* (354) nmnum ::= plus_num */
+ -1, /* (355) nmnum ::= nm */
+ -1, /* (356) nmnum ::= ON */
+ -1, /* (357) nmnum ::= DELETE */
+ -1, /* (358) nmnum ::= DEFAULT */
+ -1, /* (359) plus_num ::= INTEGER|FLOAT */
+ 0, /* (360) foreach_clause ::= */
+ -3, /* (361) foreach_clause ::= FOR EACH ROW */
+ -1, /* (362) trnm ::= nm */
+ 0, /* (363) tridxby ::= */
+ -1, /* (364) database_kw_opt ::= DATABASE */
+ 0, /* (365) database_kw_opt ::= */
+ 0, /* (366) kwcolumn_opt ::= */
+ -1, /* (367) kwcolumn_opt ::= COLUMNKW */
+ -1, /* (368) vtabarglist ::= vtabarg */
+ -3, /* (369) vtabarglist ::= vtabarglist COMMA vtabarg */
+ -2, /* (370) vtabarg ::= vtabarg vtabargtoken */
+ 0, /* (371) anylist ::= */
+ -4, /* (372) anylist ::= anylist LP anylist RP */
+ -2, /* (373) anylist ::= anylist ANY */
+ 0, /* (374) with ::= */
};
static void yy_accept(yyParser*); /* Forward Declaration */
@@ -150439,15 +151321,16 @@ static YYACTIONTYPE yy_reduce(
{ sqlite3FinishCoding(pParse); }
break;
case 3: /* cmd ::= BEGIN transtype trans_opt */
-{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy96);}
+{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy494);}
break;
case 4: /* transtype ::= */
-{yymsp[1].minor.yy96 = TK_DEFERRED;}
+{yymsp[1].minor.yy494 = TK_DEFERRED;}
break;
case 5: /* transtype ::= DEFERRED */
case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6);
case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7);
-{yymsp[0].minor.yy96 = yymsp[0].major; /*A-overwrites-X*/}
+ case 300: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==300);
+{yymsp[0].minor.yy494 = yymsp[0].major; /*A-overwrites-X*/}
break;
case 8: /* cmd ::= COMMIT|END trans_opt */
case 9: /* cmd ::= ROLLBACK trans_opt */ yytestcase(yyruleno==9);
@@ -150470,7 +151353,7 @@ static YYACTIONTYPE yy_reduce(
break;
case 13: /* create_table ::= createkw temp TABLE ifnotexists nm dbnm */
{
- sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy96,0,0,yymsp[-2].minor.yy96);
+ sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy494,0,0,yymsp[-2].minor.yy494);
}
break;
case 14: /* createkw ::= CREATE */
@@ -150485,32 +151368,32 @@ static YYACTIONTYPE yy_reduce(
case 76: /* ifexists ::= */ yytestcase(yyruleno==76);
case 93: /* distinct ::= */ yytestcase(yyruleno==93);
case 226: /* collate ::= */ yytestcase(yyruleno==226);
-{yymsp[1].minor.yy96 = 0;}
+{yymsp[1].minor.yy494 = 0;}
break;
case 16: /* ifnotexists ::= IF NOT EXISTS */
-{yymsp[-2].minor.yy96 = 1;}
+{yymsp[-2].minor.yy494 = 1;}
break;
case 17: /* temp ::= TEMP */
case 43: /* autoinc ::= AUTOINCR */ yytestcase(yyruleno==43);
-{yymsp[0].minor.yy96 = 1;}
+{yymsp[0].minor.yy494 = 1;}
break;
case 19: /* create_table_args ::= LP columnlist conslist_opt RP table_options */
{
- sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy96,0);
+ sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy494,0);
}
break;
case 20: /* create_table_args ::= AS select */
{
- sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy423);
- sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy423);
+ sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy457);
+ sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy457);
}
break;
case 22: /* table_options ::= WITHOUT nm */
{
if( yymsp[0].minor.yy0.n==5 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"rowid",5)==0 ){
- yymsp[-1].minor.yy96 = TF_WithoutRowid | TF_NoVisibleRowid;
+ yymsp[-1].minor.yy494 = TF_WithoutRowid | TF_NoVisibleRowid;
}else{
- yymsp[-1].minor.yy96 = 0;
+ yymsp[-1].minor.yy494 = 0;
sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z);
}
}
@@ -150539,7 +151422,7 @@ static YYACTIONTYPE yy_reduce(
case 28: /* scanpt ::= */
{
assert( yyLookahead!=YYNOCODE );
- yymsp[1].minor.yy464 = yyLookaheadToken.z;
+ yymsp[1].minor.yy294 = yyLookaheadToken.z;
}
break;
case 29: /* ccons ::= CONSTRAINT nm */
@@ -150547,18 +151430,18 @@ static YYACTIONTYPE yy_reduce(
{pParse->constraintName = yymsp[0].minor.yy0;}
break;
case 30: /* ccons ::= DEFAULT scanpt term scanpt */
-{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy490,yymsp[-2].minor.yy464,yymsp[0].minor.yy464);}
+{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy524,yymsp[-2].minor.yy294,yymsp[0].minor.yy294);}
break;
case 31: /* ccons ::= DEFAULT LP expr RP */
-{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy490,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);}
+{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy524,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);}
break;
case 32: /* ccons ::= DEFAULT PLUS term scanpt */
-{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy490,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy464);}
+{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy524,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy294);}
break;
case 33: /* ccons ::= DEFAULT MINUS term scanpt */
{
- Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[-1].minor.yy490, 0);
- sqlite3AddDefaultValue(pParse,p,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy464);
+ Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[-1].minor.yy524, 0);
+ sqlite3AddDefaultValue(pParse,p,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy294);
}
break;
case 34: /* ccons ::= DEFAULT scanpt ID|INDEXED */
@@ -150572,170 +151455,170 @@ static YYACTIONTYPE yy_reduce(
}
break;
case 35: /* ccons ::= NOT NULL onconf */
-{sqlite3AddNotNull(pParse, yymsp[0].minor.yy96);}
+{sqlite3AddNotNull(pParse, yymsp[0].minor.yy494);}
break;
case 36: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */
-{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy96,yymsp[0].minor.yy96,yymsp[-2].minor.yy96);}
+{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy494,yymsp[0].minor.yy494,yymsp[-2].minor.yy494);}
break;
case 37: /* ccons ::= UNIQUE onconf */
-{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy96,0,0,0,0,
+{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy494,0,0,0,0,
SQLITE_IDXTYPE_UNIQUE);}
break;
case 38: /* ccons ::= CHECK LP expr RP */
-{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy490);}
+{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy524);}
break;
case 39: /* ccons ::= REFERENCES nm eidlist_opt refargs */
-{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy42,yymsp[0].minor.yy96);}
+{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy434,yymsp[0].minor.yy494);}
break;
case 40: /* ccons ::= defer_subclause */
-{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy96);}
+{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy494);}
break;
case 41: /* ccons ::= COLLATE ID|STRING */
{sqlite3AddCollateType(pParse, &yymsp[0].minor.yy0);}
break;
case 44: /* refargs ::= */
-{ yymsp[1].minor.yy96 = OE_None*0x0101; /* EV: R-19803-45884 */}
+{ yymsp[1].minor.yy494 = OE_None*0x0101; /* EV: R-19803-45884 */}
break;
case 45: /* refargs ::= refargs refarg */
-{ yymsp[-1].minor.yy96 = (yymsp[-1].minor.yy96 & ~yymsp[0].minor.yy367.mask) | yymsp[0].minor.yy367.value; }
+{ yymsp[-1].minor.yy494 = (yymsp[-1].minor.yy494 & ~yymsp[0].minor.yy355.mask) | yymsp[0].minor.yy355.value; }
break;
case 46: /* refarg ::= MATCH nm */
-{ yymsp[-1].minor.yy367.value = 0; yymsp[-1].minor.yy367.mask = 0x000000; }
+{ yymsp[-1].minor.yy355.value = 0; yymsp[-1].minor.yy355.mask = 0x000000; }
break;
case 47: /* refarg ::= ON INSERT refact */
-{ yymsp[-2].minor.yy367.value = 0; yymsp[-2].minor.yy367.mask = 0x000000; }
+{ yymsp[-2].minor.yy355.value = 0; yymsp[-2].minor.yy355.mask = 0x000000; }
break;
case 48: /* refarg ::= ON DELETE refact */
-{ yymsp[-2].minor.yy367.value = yymsp[0].minor.yy96; yymsp[-2].minor.yy367.mask = 0x0000ff; }
+{ yymsp[-2].minor.yy355.value = yymsp[0].minor.yy494; yymsp[-2].minor.yy355.mask = 0x0000ff; }
break;
case 49: /* refarg ::= ON UPDATE refact */
-{ yymsp[-2].minor.yy367.value = yymsp[0].minor.yy96<<8; yymsp[-2].minor.yy367.mask = 0x00ff00; }
+{ yymsp[-2].minor.yy355.value = yymsp[0].minor.yy494<<8; yymsp[-2].minor.yy355.mask = 0x00ff00; }
break;
case 50: /* refact ::= SET NULL */
-{ yymsp[-1].minor.yy96 = OE_SetNull; /* EV: R-33326-45252 */}
+{ yymsp[-1].minor.yy494 = OE_SetNull; /* EV: R-33326-45252 */}
break;
case 51: /* refact ::= SET DEFAULT */
-{ yymsp[-1].minor.yy96 = OE_SetDflt; /* EV: R-33326-45252 */}
+{ yymsp[-1].minor.yy494 = OE_SetDflt; /* EV: R-33326-45252 */}
break;
case 52: /* refact ::= CASCADE */
-{ yymsp[0].minor.yy96 = OE_Cascade; /* EV: R-33326-45252 */}
+{ yymsp[0].minor.yy494 = OE_Cascade; /* EV: R-33326-45252 */}
break;
case 53: /* refact ::= RESTRICT */
-{ yymsp[0].minor.yy96 = OE_Restrict; /* EV: R-33326-45252 */}
+{ yymsp[0].minor.yy494 = OE_Restrict; /* EV: R-33326-45252 */}
break;
case 54: /* refact ::= NO ACTION */
-{ yymsp[-1].minor.yy96 = OE_None; /* EV: R-33326-45252 */}
+{ yymsp[-1].minor.yy494 = OE_None; /* EV: R-33326-45252 */}
break;
case 55: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
-{yymsp[-2].minor.yy96 = 0;}
+{yymsp[-2].minor.yy494 = 0;}
break;
case 56: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
case 71: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==71);
case 156: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==156);
-{yymsp[-1].minor.yy96 = yymsp[0].minor.yy96;}
+{yymsp[-1].minor.yy494 = yymsp[0].minor.yy494;}
break;
case 58: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */
case 75: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==75);
case 198: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==198);
case 201: /* in_op ::= NOT IN */ yytestcase(yyruleno==201);
case 227: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==227);
-{yymsp[-1].minor.yy96 = 1;}
+{yymsp[-1].minor.yy494 = 1;}
break;
case 59: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
-{yymsp[-1].minor.yy96 = 0;}
+{yymsp[-1].minor.yy494 = 0;}
break;
case 61: /* tconscomma ::= COMMA */
{pParse->constraintName.n = 0;}
break;
case 63: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
-{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy42,yymsp[0].minor.yy96,yymsp[-2].minor.yy96,0);}
+{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy434,yymsp[0].minor.yy494,yymsp[-2].minor.yy494,0);}
break;
case 64: /* tcons ::= UNIQUE LP sortlist RP onconf */
-{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy42,yymsp[0].minor.yy96,0,0,0,0,
+{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy434,yymsp[0].minor.yy494,0,0,0,0,
SQLITE_IDXTYPE_UNIQUE);}
break;
case 65: /* tcons ::= CHECK LP expr RP onconf */
-{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy490);}
+{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy524);}
break;
case 66: /* tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
{
- sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy42, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy42, yymsp[-1].minor.yy96);
- sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy96);
+ sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy434, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy434, yymsp[-1].minor.yy494);
+ sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy494);
}
break;
case 68: /* onconf ::= */
case 70: /* orconf ::= */ yytestcase(yyruleno==70);
-{yymsp[1].minor.yy96 = OE_Default;}
+{yymsp[1].minor.yy494 = OE_Default;}
break;
case 69: /* onconf ::= ON CONFLICT resolvetype */
-{yymsp[-2].minor.yy96 = yymsp[0].minor.yy96;}
+{yymsp[-2].minor.yy494 = yymsp[0].minor.yy494;}
break;
case 72: /* resolvetype ::= IGNORE */
-{yymsp[0].minor.yy96 = OE_Ignore;}
+{yymsp[0].minor.yy494 = OE_Ignore;}
break;
case 73: /* resolvetype ::= REPLACE */
case 157: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==157);
-{yymsp[0].minor.yy96 = OE_Replace;}
+{yymsp[0].minor.yy494 = OE_Replace;}
break;
case 74: /* cmd ::= DROP TABLE ifexists fullname */
{
- sqlite3DropTable(pParse, yymsp[0].minor.yy167, 0, yymsp[-1].minor.yy96);
+ sqlite3DropTable(pParse, yymsp[0].minor.yy483, 0, yymsp[-1].minor.yy494);
}
break;
case 77: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
{
- sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy42, yymsp[0].minor.yy423, yymsp[-7].minor.yy96, yymsp[-5].minor.yy96);
+ sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy434, yymsp[0].minor.yy457, yymsp[-7].minor.yy494, yymsp[-5].minor.yy494);
}
break;
case 78: /* cmd ::= DROP VIEW ifexists fullname */
{
- sqlite3DropTable(pParse, yymsp[0].minor.yy167, 1, yymsp[-1].minor.yy96);
+ sqlite3DropTable(pParse, yymsp[0].minor.yy483, 1, yymsp[-1].minor.yy494);
}
break;
case 79: /* cmd ::= select */
{
SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0};
- sqlite3Select(pParse, yymsp[0].minor.yy423, &dest);
- sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy423);
+ sqlite3Select(pParse, yymsp[0].minor.yy457, &dest);
+ sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy457);
}
break;
case 80: /* select ::= WITH wqlist selectnowith */
{
- Select *p = yymsp[0].minor.yy423;
+ Select *p = yymsp[0].minor.yy457;
if( p ){
- p->pWith = yymsp[-1].minor.yy499;
+ p->pWith = yymsp[-1].minor.yy59;
parserDoubleLinkSelect(pParse, p);
}else{
- sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy499);
+ sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy59);
}
- yymsp[-2].minor.yy423 = p;
+ yymsp[-2].minor.yy457 = p;
}
break;
case 81: /* select ::= WITH RECURSIVE wqlist selectnowith */
{
- Select *p = yymsp[0].minor.yy423;
+ Select *p = yymsp[0].minor.yy457;
if( p ){
- p->pWith = yymsp[-1].minor.yy499;
+ p->pWith = yymsp[-1].minor.yy59;
parserDoubleLinkSelect(pParse, p);
}else{
- sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy499);
+ sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy59);
}
- yymsp[-3].minor.yy423 = p;
+ yymsp[-3].minor.yy457 = p;
}
break;
case 82: /* select ::= selectnowith */
{
- Select *p = yymsp[0].minor.yy423;
+ Select *p = yymsp[0].minor.yy457;
if( p ){
parserDoubleLinkSelect(pParse, p);
}
- yymsp[0].minor.yy423 = p; /*A-overwrites-X*/
+ yymsp[0].minor.yy457 = p; /*A-overwrites-X*/
}
break;
case 83: /* selectnowith ::= selectnowith multiselect_op oneselect */
{
- Select *pRhs = yymsp[0].minor.yy423;
- Select *pLhs = yymsp[-2].minor.yy423;
+ Select *pRhs = yymsp[0].minor.yy457;
+ Select *pLhs = yymsp[-2].minor.yy457;
if( pRhs && pRhs->pPrior ){
SrcList *pFrom;
Token x;
@@ -150745,63 +151628,63 @@ static YYACTIONTYPE yy_reduce(
pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0);
}
if( pRhs ){
- pRhs->op = (u8)yymsp[-1].minor.yy96;
+ pRhs->op = (u8)yymsp[-1].minor.yy494;
pRhs->pPrior = pLhs;
if( ALWAYS(pLhs) ) pLhs->selFlags &= ~SF_MultiValue;
pRhs->selFlags &= ~SF_MultiValue;
- if( yymsp[-1].minor.yy96!=TK_ALL ) pParse->hasCompound = 1;
+ if( yymsp[-1].minor.yy494!=TK_ALL ) pParse->hasCompound = 1;
}else{
sqlite3SelectDelete(pParse->db, pLhs);
}
- yymsp[-2].minor.yy423 = pRhs;
+ yymsp[-2].minor.yy457 = pRhs;
}
break;
case 84: /* multiselect_op ::= UNION */
case 86: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==86);
-{yymsp[0].minor.yy96 = yymsp[0].major; /*A-overwrites-OP*/}
+{yymsp[0].minor.yy494 = yymsp[0].major; /*A-overwrites-OP*/}
break;
case 85: /* multiselect_op ::= UNION ALL */
-{yymsp[-1].minor.yy96 = TK_ALL;}
+{yymsp[-1].minor.yy494 = TK_ALL;}
break;
case 87: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
{
- yymsp[-8].minor.yy423 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy42,yymsp[-5].minor.yy167,yymsp[-4].minor.yy490,yymsp[-3].minor.yy42,yymsp[-2].minor.yy490,yymsp[-1].minor.yy42,yymsp[-7].minor.yy96,yymsp[0].minor.yy490);
+ yymsp[-8].minor.yy457 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy434,yymsp[-5].minor.yy483,yymsp[-4].minor.yy524,yymsp[-3].minor.yy434,yymsp[-2].minor.yy524,yymsp[-1].minor.yy434,yymsp[-7].minor.yy494,yymsp[0].minor.yy524);
}
break;
case 88: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
{
- yymsp[-9].minor.yy423 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy42,yymsp[-6].minor.yy167,yymsp[-5].minor.yy490,yymsp[-4].minor.yy42,yymsp[-3].minor.yy490,yymsp[-1].minor.yy42,yymsp[-8].minor.yy96,yymsp[0].minor.yy490);
- if( yymsp[-9].minor.yy423 ){
- yymsp[-9].minor.yy423->pWinDefn = yymsp[-2].minor.yy147;
+ yymsp[-9].minor.yy457 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy434,yymsp[-6].minor.yy483,yymsp[-5].minor.yy524,yymsp[-4].minor.yy434,yymsp[-3].minor.yy524,yymsp[-1].minor.yy434,yymsp[-8].minor.yy494,yymsp[0].minor.yy524);
+ if( yymsp[-9].minor.yy457 ){
+ yymsp[-9].minor.yy457->pWinDefn = yymsp[-2].minor.yy295;
}else{
- sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy147);
+ sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy295);
}
}
break;
case 89: /* values ::= VALUES LP nexprlist RP */
{
- yymsp[-3].minor.yy423 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy42,0,0,0,0,0,SF_Values,0);
+ yymsp[-3].minor.yy457 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy434,0,0,0,0,0,SF_Values,0);
}
break;
case 90: /* values ::= values COMMA LP nexprlist RP */
{
- Select *pRight, *pLeft = yymsp[-4].minor.yy423;
- pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy42,0,0,0,0,0,SF_Values|SF_MultiValue,0);
+ Select *pRight, *pLeft = yymsp[-4].minor.yy457;
+ pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy434,0,0,0,0,0,SF_Values|SF_MultiValue,0);
if( ALWAYS(pLeft) ) pLeft->selFlags &= ~SF_MultiValue;
if( pRight ){
pRight->op = TK_ALL;
pRight->pPrior = pLeft;
- yymsp[-4].minor.yy423 = pRight;
+ yymsp[-4].minor.yy457 = pRight;
}else{
- yymsp[-4].minor.yy423 = pLeft;
+ yymsp[-4].minor.yy457 = pLeft;
}
}
break;
case 91: /* distinct ::= DISTINCT */
-{yymsp[0].minor.yy96 = SF_Distinct;}
+{yymsp[0].minor.yy494 = SF_Distinct;}
break;
case 92: /* distinct ::= ALL */
-{yymsp[0].minor.yy96 = SF_All;}
+{yymsp[0].minor.yy494 = SF_All;}
break;
case 94: /* sclp ::= */
case 127: /* orderby_opt ::= */ yytestcase(yyruleno==127);
@@ -150809,19 +151692,19 @@ static YYACTIONTYPE yy_reduce(
case 214: /* exprlist ::= */ yytestcase(yyruleno==214);
case 217: /* paren_exprlist ::= */ yytestcase(yyruleno==217);
case 222: /* eidlist_opt ::= */ yytestcase(yyruleno==222);
-{yymsp[1].minor.yy42 = 0;}
+{yymsp[1].minor.yy434 = 0;}
break;
case 95: /* selcollist ::= sclp scanpt expr scanpt as */
{
- yymsp[-4].minor.yy42 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy42, yymsp[-2].minor.yy490);
- if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy42, &yymsp[0].minor.yy0, 1);
- sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy42,yymsp[-3].minor.yy464,yymsp[-1].minor.yy464);
+ yymsp[-4].minor.yy434 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy434, yymsp[-2].minor.yy524);
+ if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy434, &yymsp[0].minor.yy0, 1);
+ sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy434,yymsp[-3].minor.yy294,yymsp[-1].minor.yy294);
}
break;
case 96: /* selcollist ::= sclp scanpt STAR */
{
Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0);
- yymsp[-2].minor.yy42 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy42, p);
+ yymsp[-2].minor.yy434 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy434, p);
}
break;
case 97: /* selcollist ::= sclp scanpt nm DOT STAR */
@@ -150829,7 +151712,7 @@ static YYACTIONTYPE yy_reduce(
Expr *pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0);
Expr *pLeft = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1);
Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight);
- yymsp[-4].minor.yy42 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy42, pDot);
+ yymsp[-4].minor.yy434 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy434, pDot);
}
break;
case 98: /* as ::= AS nm */
@@ -150839,48 +151722,48 @@ static YYACTIONTYPE yy_reduce(
{yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;}
break;
case 100: /* from ::= */
-{yymsp[1].minor.yy167 = sqlite3DbMallocZero(pParse->db, sizeof(*yymsp[1].minor.yy167));}
+{yymsp[1].minor.yy483 = sqlite3DbMallocZero(pParse->db, sizeof(*yymsp[1].minor.yy483));}
break;
case 101: /* from ::= FROM seltablist */
{
- yymsp[-1].minor.yy167 = yymsp[0].minor.yy167;
- sqlite3SrcListShiftJoinType(yymsp[-1].minor.yy167);
+ yymsp[-1].minor.yy483 = yymsp[0].minor.yy483;
+ sqlite3SrcListShiftJoinType(yymsp[-1].minor.yy483);
}
break;
case 102: /* stl_prefix ::= seltablist joinop */
{
- if( ALWAYS(yymsp[-1].minor.yy167 && yymsp[-1].minor.yy167->nSrc>0) ) yymsp[-1].minor.yy167->a[yymsp[-1].minor.yy167->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy96;
+ if( ALWAYS(yymsp[-1].minor.yy483 && yymsp[-1].minor.yy483->nSrc>0) ) yymsp[-1].minor.yy483->a[yymsp[-1].minor.yy483->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy494;
}
break;
case 103: /* stl_prefix ::= */
-{yymsp[1].minor.yy167 = 0;}
+{yymsp[1].minor.yy483 = 0;}
break;
case 104: /* seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */
{
- yymsp[-6].minor.yy167 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy167,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy490,yymsp[0].minor.yy336);
- sqlite3SrcListIndexedBy(pParse, yymsp[-6].minor.yy167, &yymsp[-2].minor.yy0);
+ yymsp[-6].minor.yy483 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy483,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy524,yymsp[0].minor.yy62);
+ sqlite3SrcListIndexedBy(pParse, yymsp[-6].minor.yy483, &yymsp[-2].minor.yy0);
}
break;
case 105: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */
{
- yymsp[-8].minor.yy167 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-8].minor.yy167,&yymsp[-7].minor.yy0,&yymsp[-6].minor.yy0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy490,yymsp[0].minor.yy336);
- sqlite3SrcListFuncArgs(pParse, yymsp[-8].minor.yy167, yymsp[-4].minor.yy42);
+ yymsp[-8].minor.yy483 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-8].minor.yy483,&yymsp[-7].minor.yy0,&yymsp[-6].minor.yy0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy524,yymsp[0].minor.yy62);
+ sqlite3SrcListFuncArgs(pParse, yymsp[-8].minor.yy483, yymsp[-4].minor.yy434);
}
break;
case 106: /* seltablist ::= stl_prefix LP select RP as on_opt using_opt */
{
- yymsp[-6].minor.yy167 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy167,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy423,yymsp[-1].minor.yy490,yymsp[0].minor.yy336);
+ yymsp[-6].minor.yy483 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy483,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy457,yymsp[-1].minor.yy524,yymsp[0].minor.yy62);
}
break;
case 107: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */
{
- if( yymsp[-6].minor.yy167==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy490==0 && yymsp[0].minor.yy336==0 ){
- yymsp[-6].minor.yy167 = yymsp[-4].minor.yy167;
- }else if( yymsp[-4].minor.yy167->nSrc==1 ){
- yymsp[-6].minor.yy167 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy167,0,0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy490,yymsp[0].minor.yy336);
- if( yymsp[-6].minor.yy167 ){
- struct SrcList_item *pNew = &yymsp[-6].minor.yy167->a[yymsp[-6].minor.yy167->nSrc-1];
- struct SrcList_item *pOld = yymsp[-4].minor.yy167->a;
+ if( yymsp[-6].minor.yy483==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy524==0 && yymsp[0].minor.yy62==0 ){
+ yymsp[-6].minor.yy483 = yymsp[-4].minor.yy483;
+ }else if( yymsp[-4].minor.yy483->nSrc==1 ){
+ yymsp[-6].minor.yy483 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy483,0,0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy524,yymsp[0].minor.yy62);
+ if( yymsp[-6].minor.yy483 ){
+ struct SrcList_item *pNew = &yymsp[-6].minor.yy483->a[yymsp[-6].minor.yy483->nSrc-1];
+ struct SrcList_item *pOld = yymsp[-4].minor.yy483->a;
pNew->zName = pOld->zName;
pNew->zDatabase = pOld->zDatabase;
pNew->pSelect = pOld->pSelect;
@@ -150893,12 +151776,12 @@ static YYACTIONTYPE yy_reduce(
pOld->zName = pOld->zDatabase = 0;
pOld->pSelect = 0;
}
- sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy167);
+ sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy483);
}else{
Select *pSubquery;
- sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy167);
- pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy167,0,0,0,0,SF_NestedFrom,0);
- yymsp[-6].minor.yy167 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy167,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy490,yymsp[0].minor.yy336);
+ sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy483);
+ pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy483,0,0,0,0,SF_NestedFrom,0);
+ yymsp[-6].minor.yy483 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy483,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy524,yymsp[0].minor.yy62);
}
}
break;
@@ -150908,54 +151791,54 @@ static YYACTIONTYPE yy_reduce(
break;
case 110: /* fullname ::= nm */
{
- yylhsminor.yy167 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0);
- if( IN_RENAME_OBJECT && yylhsminor.yy167 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy167->a[0].zName, &yymsp[0].minor.yy0);
+ yylhsminor.yy483 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0);
+ if( IN_RENAME_OBJECT && yylhsminor.yy483 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy483->a[0].zName, &yymsp[0].minor.yy0);
}
- yymsp[0].minor.yy167 = yylhsminor.yy167;
+ yymsp[0].minor.yy483 = yylhsminor.yy483;
break;
case 111: /* fullname ::= nm DOT nm */
{
- yylhsminor.yy167 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);
- if( IN_RENAME_OBJECT && yylhsminor.yy167 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy167->a[0].zName, &yymsp[0].minor.yy0);
+ yylhsminor.yy483 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);
+ if( IN_RENAME_OBJECT && yylhsminor.yy483 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy483->a[0].zName, &yymsp[0].minor.yy0);
}
- yymsp[-2].minor.yy167 = yylhsminor.yy167;
+ yymsp[-2].minor.yy483 = yylhsminor.yy483;
break;
case 112: /* xfullname ::= nm */
-{yymsp[0].minor.yy167 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/}
+{yymsp[0].minor.yy483 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/}
break;
case 113: /* xfullname ::= nm DOT nm */
-{yymsp[-2].minor.yy167 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/}
+{yymsp[-2].minor.yy483 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
case 114: /* xfullname ::= nm DOT nm AS nm */
{
- yymsp[-4].minor.yy167 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/
- if( yymsp[-4].minor.yy167 ) yymsp[-4].minor.yy167->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
+ yymsp[-4].minor.yy483 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/
+ if( yymsp[-4].minor.yy483 ) yymsp[-4].minor.yy483->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
}
break;
case 115: /* xfullname ::= nm AS nm */
{
- yymsp[-2].minor.yy167 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/
- if( yymsp[-2].minor.yy167 ) yymsp[-2].minor.yy167->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
+ yymsp[-2].minor.yy483 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/
+ if( yymsp[-2].minor.yy483 ) yymsp[-2].minor.yy483->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
}
break;
case 116: /* joinop ::= COMMA|JOIN */
-{ yymsp[0].minor.yy96 = JT_INNER; }
+{ yymsp[0].minor.yy494 = JT_INNER; }
break;
case 117: /* joinop ::= JOIN_KW JOIN */
-{yymsp[-1].minor.yy96 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/}
+{yymsp[-1].minor.yy494 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/}
break;
case 118: /* joinop ::= JOIN_KW nm JOIN */
-{yymsp[-2].minor.yy96 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/}
+{yymsp[-2].minor.yy494 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/}
break;
case 119: /* joinop ::= JOIN_KW nm nm JOIN */
-{yymsp[-3].minor.yy96 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/}
+{yymsp[-3].minor.yy494 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/}
break;
case 120: /* on_opt ::= ON expr */
case 137: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==137);
case 144: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==144);
case 210: /* case_else ::= ELSE expr */ yytestcase(yyruleno==210);
case 231: /* vinto ::= INTO expr */ yytestcase(yyruleno==231);
-{yymsp[-1].minor.yy490 = yymsp[0].minor.yy490;}
+{yymsp[-1].minor.yy524 = yymsp[0].minor.yy524;}
break;
case 121: /* on_opt ::= */
case 136: /* having_opt ::= */ yytestcase(yyruleno==136);
@@ -150964,7 +151847,7 @@ static YYACTIONTYPE yy_reduce(
case 211: /* case_else ::= */ yytestcase(yyruleno==211);
case 213: /* case_operand ::= */ yytestcase(yyruleno==213);
case 232: /* vinto ::= */ yytestcase(yyruleno==232);
-{yymsp[1].minor.yy490 = 0;}
+{yymsp[1].minor.yy524 = 0;}
break;
case 123: /* indexed_opt ::= INDEXED BY nm */
{yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;}
@@ -150973,119 +151856,119 @@ static YYACTIONTYPE yy_reduce(
{yymsp[-1].minor.yy0.z=0; yymsp[-1].minor.yy0.n=1;}
break;
case 125: /* using_opt ::= USING LP idlist RP */
-{yymsp[-3].minor.yy336 = yymsp[-1].minor.yy336;}
+{yymsp[-3].minor.yy62 = yymsp[-1].minor.yy62;}
break;
case 126: /* using_opt ::= */
case 158: /* idlist_opt ::= */ yytestcase(yyruleno==158);
-{yymsp[1].minor.yy336 = 0;}
+{yymsp[1].minor.yy62 = 0;}
break;
case 128: /* orderby_opt ::= ORDER BY sortlist */
case 135: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==135);
-{yymsp[-2].minor.yy42 = yymsp[0].minor.yy42;}
+{yymsp[-2].minor.yy434 = yymsp[0].minor.yy434;}
break;
case 129: /* sortlist ::= sortlist COMMA expr sortorder */
{
- yymsp[-3].minor.yy42 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy42,yymsp[-1].minor.yy490);
- sqlite3ExprListSetSortOrder(yymsp[-3].minor.yy42,yymsp[0].minor.yy96);
+ yymsp[-3].minor.yy434 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy434,yymsp[-1].minor.yy524);
+ sqlite3ExprListSetSortOrder(yymsp[-3].minor.yy434,yymsp[0].minor.yy494);
}
break;
case 130: /* sortlist ::= expr sortorder */
{
- yymsp[-1].minor.yy42 = sqlite3ExprListAppend(pParse,0,yymsp[-1].minor.yy490); /*A-overwrites-Y*/
- sqlite3ExprListSetSortOrder(yymsp[-1].minor.yy42,yymsp[0].minor.yy96);
+ yymsp[-1].minor.yy434 = sqlite3ExprListAppend(pParse,0,yymsp[-1].minor.yy524); /*A-overwrites-Y*/
+ sqlite3ExprListSetSortOrder(yymsp[-1].minor.yy434,yymsp[0].minor.yy494);
}
break;
case 131: /* sortorder ::= ASC */
-{yymsp[0].minor.yy96 = SQLITE_SO_ASC;}
+{yymsp[0].minor.yy494 = SQLITE_SO_ASC;}
break;
case 132: /* sortorder ::= DESC */
-{yymsp[0].minor.yy96 = SQLITE_SO_DESC;}
+{yymsp[0].minor.yy494 = SQLITE_SO_DESC;}
break;
case 133: /* sortorder ::= */
-{yymsp[1].minor.yy96 = SQLITE_SO_UNDEFINED;}
+{yymsp[1].minor.yy494 = SQLITE_SO_UNDEFINED;}
break;
case 139: /* limit_opt ::= LIMIT expr */
-{yymsp[-1].minor.yy490 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy490,0);}
+{yymsp[-1].minor.yy524 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy524,0);}
break;
case 140: /* limit_opt ::= LIMIT expr OFFSET expr */
-{yymsp[-3].minor.yy490 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy490,yymsp[0].minor.yy490);}
+{yymsp[-3].minor.yy524 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy524,yymsp[0].minor.yy524);}
break;
case 141: /* limit_opt ::= LIMIT expr COMMA expr */
-{yymsp[-3].minor.yy490 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy490,yymsp[-2].minor.yy490);}
+{yymsp[-3].minor.yy524 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy524,yymsp[-2].minor.yy524);}
break;
case 142: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt */
{
- sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy167, &yymsp[-1].minor.yy0);
- sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy167,yymsp[0].minor.yy490,0,0);
+ sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy483, &yymsp[-1].minor.yy0);
+ sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy483,yymsp[0].minor.yy524,0,0);
}
break;
case 145: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */
{
- sqlite3SrcListIndexedBy(pParse, yymsp[-4].minor.yy167, &yymsp[-3].minor.yy0);
- sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy42,"set list");
- sqlite3Update(pParse,yymsp[-4].minor.yy167,yymsp[-1].minor.yy42,yymsp[0].minor.yy490,yymsp[-5].minor.yy96,0,0,0);
+ sqlite3SrcListIndexedBy(pParse, yymsp[-4].minor.yy483, &yymsp[-3].minor.yy0);
+ sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy434,"set list");
+ sqlite3Update(pParse,yymsp[-4].minor.yy483,yymsp[-1].minor.yy434,yymsp[0].minor.yy524,yymsp[-5].minor.yy494,0,0,0);
}
break;
case 146: /* setlist ::= setlist COMMA nm EQ expr */
{
- yymsp[-4].minor.yy42 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy42, yymsp[0].minor.yy490);
- sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy42, &yymsp[-2].minor.yy0, 1);
+ yymsp[-4].minor.yy434 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy434, yymsp[0].minor.yy524);
+ sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy434, &yymsp[-2].minor.yy0, 1);
}
break;
case 147: /* setlist ::= setlist COMMA LP idlist RP EQ expr */
{
- yymsp[-6].minor.yy42 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy42, yymsp[-3].minor.yy336, yymsp[0].minor.yy490);
+ yymsp[-6].minor.yy434 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy434, yymsp[-3].minor.yy62, yymsp[0].minor.yy524);
}
break;
case 148: /* setlist ::= nm EQ expr */
{
- yylhsminor.yy42 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy490);
- sqlite3ExprListSetName(pParse, yylhsminor.yy42, &yymsp[-2].minor.yy0, 1);
+ yylhsminor.yy434 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy524);
+ sqlite3ExprListSetName(pParse, yylhsminor.yy434, &yymsp[-2].minor.yy0, 1);
}
- yymsp[-2].minor.yy42 = yylhsminor.yy42;
+ yymsp[-2].minor.yy434 = yylhsminor.yy434;
break;
case 149: /* setlist ::= LP idlist RP EQ expr */
{
- yymsp[-4].minor.yy42 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy336, yymsp[0].minor.yy490);
+ yymsp[-4].minor.yy434 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy62, yymsp[0].minor.yy524);
}
break;
case 150: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
{
- sqlite3Insert(pParse, yymsp[-3].minor.yy167, yymsp[-1].minor.yy423, yymsp[-2].minor.yy336, yymsp[-5].minor.yy96, yymsp[0].minor.yy266);
+ sqlite3Insert(pParse, yymsp[-3].minor.yy483, yymsp[-1].minor.yy457, yymsp[-2].minor.yy62, yymsp[-5].minor.yy494, yymsp[0].minor.yy136);
}
break;
case 151: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */
{
- sqlite3Insert(pParse, yymsp[-3].minor.yy167, 0, yymsp[-2].minor.yy336, yymsp[-5].minor.yy96, 0);
+ sqlite3Insert(pParse, yymsp[-3].minor.yy483, 0, yymsp[-2].minor.yy62, yymsp[-5].minor.yy494, 0);
}
break;
case 152: /* upsert ::= */
-{ yymsp[1].minor.yy266 = 0; }
+{ yymsp[1].minor.yy136 = 0; }
break;
case 153: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */
-{ yymsp[-10].minor.yy266 = sqlite3UpsertNew(pParse->db,yymsp[-7].minor.yy42,yymsp[-5].minor.yy490,yymsp[-1].minor.yy42,yymsp[0].minor.yy490);}
+{ yymsp[-10].minor.yy136 = sqlite3UpsertNew(pParse->db,yymsp[-7].minor.yy434,yymsp[-5].minor.yy524,yymsp[-1].minor.yy434,yymsp[0].minor.yy524);}
break;
case 154: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */
-{ yymsp[-7].minor.yy266 = sqlite3UpsertNew(pParse->db,yymsp[-4].minor.yy42,yymsp[-2].minor.yy490,0,0); }
+{ yymsp[-7].minor.yy136 = sqlite3UpsertNew(pParse->db,yymsp[-4].minor.yy434,yymsp[-2].minor.yy524,0,0); }
break;
case 155: /* upsert ::= ON CONFLICT DO NOTHING */
-{ yymsp[-3].minor.yy266 = sqlite3UpsertNew(pParse->db,0,0,0,0); }
+{ yymsp[-3].minor.yy136 = sqlite3UpsertNew(pParse->db,0,0,0,0); }
break;
case 159: /* idlist_opt ::= LP idlist RP */
-{yymsp[-2].minor.yy336 = yymsp[-1].minor.yy336;}
+{yymsp[-2].minor.yy62 = yymsp[-1].minor.yy62;}
break;
case 160: /* idlist ::= idlist COMMA nm */
-{yymsp[-2].minor.yy336 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy336,&yymsp[0].minor.yy0);}
+{yymsp[-2].minor.yy62 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy62,&yymsp[0].minor.yy0);}
break;
case 161: /* idlist ::= nm */
-{yymsp[0].minor.yy336 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/}
+{yymsp[0].minor.yy62 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/}
break;
case 162: /* expr ::= LP expr RP */
-{yymsp[-2].minor.yy490 = yymsp[-1].minor.yy490;}
+{yymsp[-2].minor.yy524 = yymsp[-1].minor.yy524;}
break;
case 163: /* expr ::= ID|INDEXED */
case 164: /* expr ::= JOIN_KW */ yytestcase(yyruleno==164);
-{yymsp[0].minor.yy490=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/}
+{yymsp[0].minor.yy524=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
case 165: /* expr ::= nm DOT nm */
{
@@ -151095,9 +151978,9 @@ static YYACTIONTYPE yy_reduce(
sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[0].minor.yy0);
sqlite3RenameTokenMap(pParse, (void*)temp1, &yymsp[-2].minor.yy0);
}
- yylhsminor.yy490 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2);
+ yylhsminor.yy524 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2);
}
- yymsp[-2].minor.yy490 = yylhsminor.yy490;
+ yymsp[-2].minor.yy524 = yylhsminor.yy524;
break;
case 166: /* expr ::= nm DOT nm DOT nm */
{
@@ -151109,26 +151992,26 @@ static YYACTIONTYPE yy_reduce(
sqlite3RenameTokenMap(pParse, (void*)temp3, &yymsp[0].minor.yy0);
sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[-2].minor.yy0);
}
- yylhsminor.yy490 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4);
+ yylhsminor.yy524 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4);
}
- yymsp[-4].minor.yy490 = yylhsminor.yy490;
+ yymsp[-4].minor.yy524 = yylhsminor.yy524;
break;
case 167: /* term ::= NULL|FLOAT|BLOB */
case 168: /* term ::= STRING */ yytestcase(yyruleno==168);
-{yymsp[0].minor.yy490=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/}
+{yymsp[0].minor.yy524=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
case 169: /* term ::= INTEGER */
{
- yylhsminor.yy490 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1);
+ yylhsminor.yy524 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1);
}
- yymsp[0].minor.yy490 = yylhsminor.yy490;
+ yymsp[0].minor.yy524 = yylhsminor.yy524;
break;
case 170: /* expr ::= VARIABLE */
{
if( !(yymsp[0].minor.yy0.z[0]=='#' && sqlite3Isdigit(yymsp[0].minor.yy0.z[1])) ){
u32 n = yymsp[0].minor.yy0.n;
- yymsp[0].minor.yy490 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0);
- sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy490, n);
+ yymsp[0].minor.yy524 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0);
+ sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy524, n);
}else{
/* When doing a nested parse, one can include terms in an expression
** that look like this: #1 #2 ... These terms refer to registers
@@ -151137,63 +152020,63 @@ static YYACTIONTYPE yy_reduce(
assert( t.n>=2 );
if( pParse->nested==0 ){
sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t);
- yymsp[0].minor.yy490 = 0;
+ yymsp[0].minor.yy524 = 0;
}else{
- yymsp[0].minor.yy490 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
- if( yymsp[0].minor.yy490 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy490->iTable);
+ yymsp[0].minor.yy524 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
+ if( yymsp[0].minor.yy524 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy524->iTable);
}
}
}
break;
case 171: /* expr ::= expr COLLATE ID|STRING */
{
- yymsp[-2].minor.yy490 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy490, &yymsp[0].minor.yy0, 1);
+ yymsp[-2].minor.yy524 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy524, &yymsp[0].minor.yy0, 1);
}
break;
case 172: /* expr ::= CAST LP expr AS typetoken RP */
{
- yymsp[-5].minor.yy490 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1);
- sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy490, yymsp[-3].minor.yy490, 0);
+ yymsp[-5].minor.yy524 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1);
+ sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy524, yymsp[-3].minor.yy524, 0);
}
break;
case 173: /* expr ::= ID|INDEXED LP distinct exprlist RP */
{
- yylhsminor.yy490 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy42, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy96);
+ yylhsminor.yy524 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy434, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy494);
}
- yymsp[-4].minor.yy490 = yylhsminor.yy490;
+ yymsp[-4].minor.yy524 = yylhsminor.yy524;
break;
case 174: /* expr ::= ID|INDEXED LP STAR RP */
{
- yylhsminor.yy490 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0);
+ yylhsminor.yy524 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0);
}
- yymsp[-3].minor.yy490 = yylhsminor.yy490;
+ yymsp[-3].minor.yy524 = yylhsminor.yy524;
break;
case 175: /* expr ::= ID|INDEXED LP distinct exprlist RP over_clause */
{
- yylhsminor.yy490 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy42, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy96);
- sqlite3WindowAttach(pParse, yylhsminor.yy490, yymsp[0].minor.yy147);
+ yylhsminor.yy524 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy434, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy494);
+ sqlite3WindowAttach(pParse, yylhsminor.yy524, yymsp[0].minor.yy295);
}
- yymsp[-5].minor.yy490 = yylhsminor.yy490;
+ yymsp[-5].minor.yy524 = yylhsminor.yy524;
break;
case 176: /* expr ::= ID|INDEXED LP STAR RP over_clause */
{
- yylhsminor.yy490 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0);
- sqlite3WindowAttach(pParse, yylhsminor.yy490, yymsp[0].minor.yy147);
+ yylhsminor.yy524 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0);
+ sqlite3WindowAttach(pParse, yylhsminor.yy524, yymsp[0].minor.yy295);
}
- yymsp[-4].minor.yy490 = yylhsminor.yy490;
+ yymsp[-4].minor.yy524 = yylhsminor.yy524;
break;
case 177: /* term ::= CTIME_KW */
{
- yylhsminor.yy490 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0);
+ yylhsminor.yy524 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0);
}
- yymsp[0].minor.yy490 = yylhsminor.yy490;
+ yymsp[0].minor.yy524 = yylhsminor.yy524;
break;
case 178: /* expr ::= LP nexprlist COMMA expr RP */
{
- ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy42, yymsp[-1].minor.yy490);
- yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
- if( yymsp[-4].minor.yy490 ){
- yymsp[-4].minor.yy490->x.pList = pList;
+ ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy434, yymsp[-1].minor.yy524);
+ yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
+ if( yymsp[-4].minor.yy524 ){
+ yymsp[-4].minor.yy524->x.pList = pList;
}else{
sqlite3ExprListDelete(pParse->db, pList);
}
@@ -151207,7 +152090,7 @@ static YYACTIONTYPE yy_reduce(
case 184: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==184);
case 185: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==185);
case 186: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==186);
-{yymsp[-2].minor.yy490=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy490,yymsp[0].minor.yy490);}
+{yymsp[-2].minor.yy524=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy524,yymsp[0].minor.yy524);}
break;
case 187: /* likeop ::= NOT LIKE_KW|MATCH */
{yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.n|=0x80000000; /*yymsp[-1].minor.yy0-overwrite-yymsp[0].minor.yy0*/}
@@ -151217,11 +152100,11 @@ static YYACTIONTYPE yy_reduce(
ExprList *pList;
int bNot = yymsp[-1].minor.yy0.n & 0x80000000;
yymsp[-1].minor.yy0.n &= 0x7fffffff;
- pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy490);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy490);
- yymsp[-2].minor.yy490 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
- if( bNot ) yymsp[-2].minor.yy490 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy490, 0);
- if( yymsp[-2].minor.yy490 ) yymsp[-2].minor.yy490->flags |= EP_InfixFunc;
+ pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy524);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy524);
+ yymsp[-2].minor.yy524 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
+ if( bNot ) yymsp[-2].minor.yy524 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy524, 0);
+ if( yymsp[-2].minor.yy524 ) yymsp[-2].minor.yy524->flags |= EP_InfixFunc;
}
break;
case 189: /* expr ::= expr likeop expr ESCAPE expr */
@@ -151229,62 +152112,62 @@ static YYACTIONTYPE yy_reduce(
ExprList *pList;
int bNot = yymsp[-3].minor.yy0.n & 0x80000000;
yymsp[-3].minor.yy0.n &= 0x7fffffff;
- pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy490);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy490);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy490);
- yymsp[-4].minor.yy490 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0);
- if( bNot ) yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy490, 0);
- if( yymsp[-4].minor.yy490 ) yymsp[-4].minor.yy490->flags |= EP_InfixFunc;
+ pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy524);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy524);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy524);
+ yymsp[-4].minor.yy524 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0);
+ if( bNot ) yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy524, 0);
+ if( yymsp[-4].minor.yy524 ) yymsp[-4].minor.yy524->flags |= EP_InfixFunc;
}
break;
case 190: /* expr ::= expr ISNULL|NOTNULL */
-{yymsp[-1].minor.yy490 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy490,0);}
+{yymsp[-1].minor.yy524 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy524,0);}
break;
case 191: /* expr ::= expr NOT NULL */
-{yymsp[-2].minor.yy490 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy490,0);}
+{yymsp[-2].minor.yy524 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy524,0);}
break;
case 192: /* expr ::= expr IS expr */
{
- yymsp[-2].minor.yy490 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy490,yymsp[0].minor.yy490);
- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy490, yymsp[-2].minor.yy490, TK_ISNULL);
+ yymsp[-2].minor.yy524 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy524,yymsp[0].minor.yy524);
+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy524, yymsp[-2].minor.yy524, TK_ISNULL);
}
break;
case 193: /* expr ::= expr IS NOT expr */
{
- yymsp[-3].minor.yy490 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy490,yymsp[0].minor.yy490);
- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy490, yymsp[-3].minor.yy490, TK_NOTNULL);
+ yymsp[-3].minor.yy524 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy524,yymsp[0].minor.yy524);
+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy524, yymsp[-3].minor.yy524, TK_NOTNULL);
}
break;
case 194: /* expr ::= NOT expr */
case 195: /* expr ::= BITNOT expr */ yytestcase(yyruleno==195);
-{yymsp[-1].minor.yy490 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy490, 0);/*A-overwrites-B*/}
+{yymsp[-1].minor.yy524 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy524, 0);/*A-overwrites-B*/}
break;
case 196: /* expr ::= PLUS|MINUS expr */
{
- yymsp[-1].minor.yy490 = sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy490, 0);
+ yymsp[-1].minor.yy524 = sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy524, 0);
/*A-overwrites-B*/
}
break;
case 197: /* between_op ::= BETWEEN */
case 200: /* in_op ::= IN */ yytestcase(yyruleno==200);
-{yymsp[0].minor.yy96 = 0;}
+{yymsp[0].minor.yy494 = 0;}
break;
case 199: /* expr ::= expr between_op expr AND expr */
{
- ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy490);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy490);
- yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy490, 0);
- if( yymsp[-4].minor.yy490 ){
- yymsp[-4].minor.yy490->x.pList = pList;
+ ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy524);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy524);
+ yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy524, 0);
+ if( yymsp[-4].minor.yy524 ){
+ yymsp[-4].minor.yy524->x.pList = pList;
}else{
sqlite3ExprListDelete(pParse->db, pList);
}
- if( yymsp[-3].minor.yy96 ) yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy490, 0);
+ if( yymsp[-3].minor.yy494 ) yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy524, 0);
}
break;
case 202: /* expr ::= expr in_op LP exprlist RP */
{
- if( yymsp[-1].minor.yy42==0 ){
+ if( yymsp[-1].minor.yy434==0 ){
/* Expressions of the form
**
** expr1 IN ()
@@ -151294,10 +152177,10 @@ static YYACTIONTYPE yy_reduce(
** regardless of the value of expr1.
*/
if( IN_RENAME_OBJECT==0 ){
- sqlite3ExprDelete(pParse->db, yymsp[-4].minor.yy490);
- yymsp[-4].minor.yy490 = sqlite3ExprAlloc(pParse->db, TK_INTEGER,&sqlite3IntTokens[yymsp[-3].minor.yy96],1);
+ sqlite3ExprDelete(pParse->db, yymsp[-4].minor.yy524);
+ yymsp[-4].minor.yy524 = sqlite3ExprAlloc(pParse->db, TK_INTEGER,&sqlite3IntTokens[yymsp[-3].minor.yy494],1);
}
- }else if( yymsp[-1].minor.yy42->nExpr==1 ){
+ }else if( yymsp[-1].minor.yy434->nExpr==1 ){
/* Expressions of the form:
**
** expr1 IN (?1)
@@ -151314,100 +152197,100 @@ static YYACTIONTYPE yy_reduce(
** affinity or the collating sequence to use for comparison. Otherwise,
** the semantics would be subtly different from IN or NOT IN.
*/
- Expr *pRHS = yymsp[-1].minor.yy42->a[0].pExpr;
- yymsp[-1].minor.yy42->a[0].pExpr = 0;
- sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy42);
+ Expr *pRHS = yymsp[-1].minor.yy434->a[0].pExpr;
+ yymsp[-1].minor.yy434->a[0].pExpr = 0;
+ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy434);
/* pRHS cannot be NULL because a malloc error would have been detected
** before now and control would have never reached this point */
if( ALWAYS(pRHS) ){
pRHS->flags &= ~EP_Collate;
pRHS->flags |= EP_Generic;
}
- yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, yymsp[-3].minor.yy96 ? TK_NE : TK_EQ, yymsp[-4].minor.yy490, pRHS);
+ yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, yymsp[-3].minor.yy494 ? TK_NE : TK_EQ, yymsp[-4].minor.yy524, pRHS);
}else{
- yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy490, 0);
- if( yymsp[-4].minor.yy490 ){
- yymsp[-4].minor.yy490->x.pList = yymsp[-1].minor.yy42;
- sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy490);
+ yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy524, 0);
+ if( yymsp[-4].minor.yy524 ){
+ yymsp[-4].minor.yy524->x.pList = yymsp[-1].minor.yy434;
+ sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy524);
}else{
- sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy42);
+ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy434);
}
- if( yymsp[-3].minor.yy96 ) yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy490, 0);
+ if( yymsp[-3].minor.yy494 ) yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy524, 0);
}
}
break;
case 203: /* expr ::= LP select RP */
{
- yymsp[-2].minor.yy490 = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
- sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy490, yymsp[-1].minor.yy423);
+ yymsp[-2].minor.yy524 = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy524, yymsp[-1].minor.yy457);
}
break;
case 204: /* expr ::= expr in_op LP select RP */
{
- yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy490, 0);
- sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy490, yymsp[-1].minor.yy423);
- if( yymsp[-3].minor.yy96 ) yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy490, 0);
+ yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy524, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy524, yymsp[-1].minor.yy457);
+ if( yymsp[-3].minor.yy494 ) yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy524, 0);
}
break;
case 205: /* expr ::= expr in_op nm dbnm paren_exprlist */
{
SrcList *pSrc = sqlite3SrcListAppend(pParse, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);
Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0);
- if( yymsp[0].minor.yy42 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy42);
- yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy490, 0);
- sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy490, pSelect);
- if( yymsp[-3].minor.yy96 ) yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy490, 0);
+ if( yymsp[0].minor.yy434 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy434);
+ yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy524, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy524, pSelect);
+ if( yymsp[-3].minor.yy494 ) yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy524, 0);
}
break;
case 206: /* expr ::= EXISTS LP select RP */
{
Expr *p;
- p = yymsp[-3].minor.yy490 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0);
- sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy423);
+ p = yymsp[-3].minor.yy524 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0);
+ sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy457);
}
break;
case 207: /* expr ::= CASE case_operand case_exprlist case_else END */
{
- yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy490, 0);
- if( yymsp[-4].minor.yy490 ){
- yymsp[-4].minor.yy490->x.pList = yymsp[-1].minor.yy490 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy42,yymsp[-1].minor.yy490) : yymsp[-2].minor.yy42;
- sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy490);
+ yymsp[-4].minor.yy524 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy524, 0);
+ if( yymsp[-4].minor.yy524 ){
+ yymsp[-4].minor.yy524->x.pList = yymsp[-1].minor.yy524 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy434,yymsp[-1].minor.yy524) : yymsp[-2].minor.yy434;
+ sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy524);
}else{
- sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy42);
- sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy490);
+ sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy434);
+ sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy524);
}
}
break;
case 208: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */
{
- yymsp[-4].minor.yy42 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy42, yymsp[-2].minor.yy490);
- yymsp[-4].minor.yy42 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy42, yymsp[0].minor.yy490);
+ yymsp[-4].minor.yy434 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy434, yymsp[-2].minor.yy524);
+ yymsp[-4].minor.yy434 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy434, yymsp[0].minor.yy524);
}
break;
case 209: /* case_exprlist ::= WHEN expr THEN expr */
{
- yymsp[-3].minor.yy42 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy490);
- yymsp[-3].minor.yy42 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy42, yymsp[0].minor.yy490);
+ yymsp[-3].minor.yy434 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy524);
+ yymsp[-3].minor.yy434 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy434, yymsp[0].minor.yy524);
}
break;
case 212: /* case_operand ::= expr */
-{yymsp[0].minor.yy490 = yymsp[0].minor.yy490; /*A-overwrites-X*/}
+{yymsp[0].minor.yy524 = yymsp[0].minor.yy524; /*A-overwrites-X*/}
break;
case 215: /* nexprlist ::= nexprlist COMMA expr */
-{yymsp[-2].minor.yy42 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy42,yymsp[0].minor.yy490);}
+{yymsp[-2].minor.yy434 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy434,yymsp[0].minor.yy524);}
break;
case 216: /* nexprlist ::= expr */
-{yymsp[0].minor.yy42 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy490); /*A-overwrites-Y*/}
+{yymsp[0].minor.yy434 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy524); /*A-overwrites-Y*/}
break;
case 218: /* paren_exprlist ::= LP exprlist RP */
case 223: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==223);
-{yymsp[-2].minor.yy42 = yymsp[-1].minor.yy42;}
+{yymsp[-2].minor.yy434 = yymsp[-1].minor.yy434;}
break;
case 219: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
{
sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0,
- sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy42, yymsp[-10].minor.yy96,
- &yymsp[-11].minor.yy0, yymsp[0].minor.yy490, SQLITE_SO_ASC, yymsp[-8].minor.yy96, SQLITE_IDXTYPE_APPDEF);
+ sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy434, yymsp[-10].minor.yy494,
+ &yymsp[-11].minor.yy0, yymsp[0].minor.yy524, SQLITE_SO_ASC, yymsp[-8].minor.yy494, SQLITE_IDXTYPE_APPDEF);
if( IN_RENAME_OBJECT && pParse->pNewIndex ){
sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &yymsp[-4].minor.yy0);
}
@@ -151415,29 +152298,29 @@ static YYACTIONTYPE yy_reduce(
break;
case 220: /* uniqueflag ::= UNIQUE */
case 262: /* raisetype ::= ABORT */ yytestcase(yyruleno==262);
-{yymsp[0].minor.yy96 = OE_Abort;}
+{yymsp[0].minor.yy494 = OE_Abort;}
break;
case 221: /* uniqueflag ::= */
-{yymsp[1].minor.yy96 = OE_None;}
+{yymsp[1].minor.yy494 = OE_None;}
break;
case 224: /* eidlist ::= eidlist COMMA nm collate sortorder */
{
- yymsp[-4].minor.yy42 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy42, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy96, yymsp[0].minor.yy96);
+ yymsp[-4].minor.yy434 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy434, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy494, yymsp[0].minor.yy494);
}
break;
case 225: /* eidlist ::= nm collate sortorder */
{
- yymsp[-2].minor.yy42 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy96, yymsp[0].minor.yy96); /*A-overwrites-Y*/
+ yymsp[-2].minor.yy434 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy494, yymsp[0].minor.yy494); /*A-overwrites-Y*/
}
break;
case 228: /* cmd ::= DROP INDEX ifexists fullname */
-{sqlite3DropIndex(pParse, yymsp[0].minor.yy167, yymsp[-1].minor.yy96);}
+{sqlite3DropIndex(pParse, yymsp[0].minor.yy483, yymsp[-1].minor.yy494);}
break;
case 229: /* cmd ::= VACUUM vinto */
-{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy490);}
+{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy524);}
break;
case 230: /* cmd ::= VACUUM nm vinto */
-{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy490);}
+{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy524);}
break;
case 233: /* cmd ::= PRAGMA nm dbnm */
{sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);}
@@ -151459,51 +152342,51 @@ static YYACTIONTYPE yy_reduce(
Token all;
all.z = yymsp[-3].minor.yy0.z;
all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n;
- sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy119, &all);
+ sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy455, &all);
}
break;
case 241: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
{
- sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy96, yymsp[-4].minor.yy350.a, yymsp[-4].minor.yy350.b, yymsp[-2].minor.yy167, yymsp[0].minor.yy490, yymsp[-10].minor.yy96, yymsp[-8].minor.yy96);
+ sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy494, yymsp[-4].minor.yy90.a, yymsp[-4].minor.yy90.b, yymsp[-2].minor.yy483, yymsp[0].minor.yy524, yymsp[-10].minor.yy494, yymsp[-8].minor.yy494);
yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/
}
break;
case 242: /* trigger_time ::= BEFORE|AFTER */
-{ yymsp[0].minor.yy96 = yymsp[0].major; /*A-overwrites-X*/ }
+{ yymsp[0].minor.yy494 = yymsp[0].major; /*A-overwrites-X*/ }
break;
case 243: /* trigger_time ::= INSTEAD OF */
-{ yymsp[-1].minor.yy96 = TK_INSTEAD;}
+{ yymsp[-1].minor.yy494 = TK_INSTEAD;}
break;
case 244: /* trigger_time ::= */
-{ yymsp[1].minor.yy96 = TK_BEFORE; }
+{ yymsp[1].minor.yy494 = TK_BEFORE; }
break;
case 245: /* trigger_event ::= DELETE|INSERT */
case 246: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==246);
-{yymsp[0].minor.yy350.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy350.b = 0;}
+{yymsp[0].minor.yy90.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy90.b = 0;}
break;
case 247: /* trigger_event ::= UPDATE OF idlist */
-{yymsp[-2].minor.yy350.a = TK_UPDATE; yymsp[-2].minor.yy350.b = yymsp[0].minor.yy336;}
+{yymsp[-2].minor.yy90.a = TK_UPDATE; yymsp[-2].minor.yy90.b = yymsp[0].minor.yy62;}
break;
case 248: /* when_clause ::= */
case 267: /* key_opt ::= */ yytestcase(yyruleno==267);
- case 309: /* filter_opt ::= */ yytestcase(yyruleno==309);
-{ yymsp[1].minor.yy490 = 0; }
+ case 315: /* filter_opt ::= */ yytestcase(yyruleno==315);
+{ yymsp[1].minor.yy524 = 0; }
break;
case 249: /* when_clause ::= WHEN expr */
case 268: /* key_opt ::= KEY expr */ yytestcase(yyruleno==268);
-{ yymsp[-1].minor.yy490 = yymsp[0].minor.yy490; }
+{ yymsp[-1].minor.yy524 = yymsp[0].minor.yy524; }
break;
case 250: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
{
- assert( yymsp[-2].minor.yy119!=0 );
- yymsp[-2].minor.yy119->pLast->pNext = yymsp[-1].minor.yy119;
- yymsp[-2].minor.yy119->pLast = yymsp[-1].minor.yy119;
+ assert( yymsp[-2].minor.yy455!=0 );
+ yymsp[-2].minor.yy455->pLast->pNext = yymsp[-1].minor.yy455;
+ yymsp[-2].minor.yy455->pLast = yymsp[-1].minor.yy455;
}
break;
case 251: /* trigger_cmd_list ::= trigger_cmd SEMI */
{
- assert( yymsp[-1].minor.yy119!=0 );
- yymsp[-1].minor.yy119->pLast = yymsp[-1].minor.yy119;
+ assert( yymsp[-1].minor.yy455!=0 );
+ yymsp[-1].minor.yy455->pLast = yymsp[-1].minor.yy455;
}
break;
case 252: /* trnm ::= nm DOT nm */
@@ -151529,58 +152412,58 @@ static YYACTIONTYPE yy_reduce(
}
break;
case 255: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */
-{yylhsminor.yy119 = sqlite3TriggerUpdateStep(pParse, &yymsp[-5].minor.yy0, yymsp[-2].minor.yy42, yymsp[-1].minor.yy490, yymsp[-6].minor.yy96, yymsp[-7].minor.yy0.z, yymsp[0].minor.yy464);}
- yymsp[-7].minor.yy119 = yylhsminor.yy119;
+{yylhsminor.yy455 = sqlite3TriggerUpdateStep(pParse, &yymsp[-5].minor.yy0, yymsp[-2].minor.yy434, yymsp[-1].minor.yy524, yymsp[-6].minor.yy494, yymsp[-7].minor.yy0.z, yymsp[0].minor.yy294);}
+ yymsp[-7].minor.yy455 = yylhsminor.yy455;
break;
case 256: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
{
- yylhsminor.yy119 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy336,yymsp[-2].minor.yy423,yymsp[-6].minor.yy96,yymsp[-1].minor.yy266,yymsp[-7].minor.yy464,yymsp[0].minor.yy464);/*yylhsminor.yy119-overwrites-yymsp[-6].minor.yy96*/
+ yylhsminor.yy455 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy62,yymsp[-2].minor.yy457,yymsp[-6].minor.yy494,yymsp[-1].minor.yy136,yymsp[-7].minor.yy294,yymsp[0].minor.yy294);/*yylhsminor.yy455-overwrites-yymsp[-6].minor.yy494*/
}
- yymsp[-7].minor.yy119 = yylhsminor.yy119;
+ yymsp[-7].minor.yy455 = yylhsminor.yy455;
break;
case 257: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
-{yylhsminor.yy119 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy490, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy464);}
- yymsp[-5].minor.yy119 = yylhsminor.yy119;
+{yylhsminor.yy455 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy524, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy294);}
+ yymsp[-5].minor.yy455 = yylhsminor.yy455;
break;
case 258: /* trigger_cmd ::= scanpt select scanpt */
-{yylhsminor.yy119 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy423, yymsp[-2].minor.yy464, yymsp[0].minor.yy464); /*yylhsminor.yy119-overwrites-yymsp[-1].minor.yy423*/}
- yymsp[-2].minor.yy119 = yylhsminor.yy119;
+{yylhsminor.yy455 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy457, yymsp[-2].minor.yy294, yymsp[0].minor.yy294); /*yylhsminor.yy455-overwrites-yymsp[-1].minor.yy457*/}
+ yymsp[-2].minor.yy455 = yylhsminor.yy455;
break;
case 259: /* expr ::= RAISE LP IGNORE RP */
{
- yymsp[-3].minor.yy490 = sqlite3PExpr(pParse, TK_RAISE, 0, 0);
- if( yymsp[-3].minor.yy490 ){
- yymsp[-3].minor.yy490->affinity = OE_Ignore;
+ yymsp[-3].minor.yy524 = sqlite3PExpr(pParse, TK_RAISE, 0, 0);
+ if( yymsp[-3].minor.yy524 ){
+ yymsp[-3].minor.yy524->affinity = OE_Ignore;
}
}
break;
case 260: /* expr ::= RAISE LP raisetype COMMA nm RP */
{
- yymsp[-5].minor.yy490 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1);
- if( yymsp[-5].minor.yy490 ) {
- yymsp[-5].minor.yy490->affinity = (char)yymsp[-3].minor.yy96;
+ yymsp[-5].minor.yy524 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1);
+ if( yymsp[-5].minor.yy524 ) {
+ yymsp[-5].minor.yy524->affinity = (char)yymsp[-3].minor.yy494;
}
}
break;
case 261: /* raisetype ::= ROLLBACK */
-{yymsp[0].minor.yy96 = OE_Rollback;}
+{yymsp[0].minor.yy494 = OE_Rollback;}
break;
case 263: /* raisetype ::= FAIL */
-{yymsp[0].minor.yy96 = OE_Fail;}
+{yymsp[0].minor.yy494 = OE_Fail;}
break;
case 264: /* cmd ::= DROP TRIGGER ifexists fullname */
{
- sqlite3DropTrigger(pParse,yymsp[0].minor.yy167,yymsp[-1].minor.yy96);
+ sqlite3DropTrigger(pParse,yymsp[0].minor.yy483,yymsp[-1].minor.yy494);
}
break;
case 265: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
{
- sqlite3Attach(pParse, yymsp[-3].minor.yy490, yymsp[-1].minor.yy490, yymsp[0].minor.yy490);
+ sqlite3Attach(pParse, yymsp[-3].minor.yy524, yymsp[-1].minor.yy524, yymsp[0].minor.yy524);
}
break;
case 266: /* cmd ::= DETACH database_kw_opt expr */
{
- sqlite3Detach(pParse, yymsp[0].minor.yy490);
+ sqlite3Detach(pParse, yymsp[0].minor.yy524);
}
break;
case 269: /* cmd ::= REINDEX */
@@ -151597,7 +152480,7 @@ static YYACTIONTYPE yy_reduce(
break;
case 273: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
{
- sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy167,&yymsp[0].minor.yy0);
+ sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy483,&yymsp[0].minor.yy0);
}
break;
case 274: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
@@ -151609,12 +152492,12 @@ static YYACTIONTYPE yy_reduce(
case 275: /* add_column_fullname ::= fullname */
{
disableLookaside(pParse);
- sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy167);
+ sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy483);
}
break;
case 276: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
{
- sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy167, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0);
+ sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy483, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0);
}
break;
case 277: /* cmd ::= create_vtab */
@@ -151625,7 +152508,7 @@ static YYACTIONTYPE yy_reduce(
break;
case 279: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
{
- sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy96);
+ sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy494);
}
break;
case 280: /* vtabarg ::= */
@@ -151638,182 +152521,204 @@ static YYACTIONTYPE yy_reduce(
break;
case 284: /* with ::= WITH wqlist */
case 285: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==285);
-{ sqlite3WithPush(pParse, yymsp[0].minor.yy499, 1); }
+{ sqlite3WithPush(pParse, yymsp[0].minor.yy59, 1); }
break;
case 286: /* wqlist ::= nm eidlist_opt AS LP select RP */
{
- yymsp[-5].minor.yy499 = sqlite3WithAdd(pParse, 0, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy42, yymsp[-1].minor.yy423); /*A-overwrites-X*/
+ yymsp[-5].minor.yy59 = sqlite3WithAdd(pParse, 0, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy434, yymsp[-1].minor.yy457); /*A-overwrites-X*/
}
break;
case 287: /* wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
{
- yymsp[-7].minor.yy499 = sqlite3WithAdd(pParse, yymsp[-7].minor.yy499, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy42, yymsp[-1].minor.yy423);
+ yymsp[-7].minor.yy59 = sqlite3WithAdd(pParse, yymsp[-7].minor.yy59, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy434, yymsp[-1].minor.yy457);
}
break;
case 288: /* windowdefn_list ::= windowdefn */
-{ yylhsminor.yy147 = yymsp[0].minor.yy147; }
- yymsp[0].minor.yy147 = yylhsminor.yy147;
+{ yylhsminor.yy295 = yymsp[0].minor.yy295; }
+ yymsp[0].minor.yy295 = yylhsminor.yy295;
break;
case 289: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */
{
- assert( yymsp[0].minor.yy147!=0 );
- yymsp[0].minor.yy147->pNextWin = yymsp[-2].minor.yy147;
- yylhsminor.yy147 = yymsp[0].minor.yy147;
+ assert( yymsp[0].minor.yy295!=0 );
+ sqlite3WindowChain(pParse, yymsp[0].minor.yy295, yymsp[-2].minor.yy295);
+ yymsp[0].minor.yy295->pNextWin = yymsp[-2].minor.yy295;
+ yylhsminor.yy295 = yymsp[0].minor.yy295;
}
- yymsp[-2].minor.yy147 = yylhsminor.yy147;
+ yymsp[-2].minor.yy295 = yylhsminor.yy295;
break;
- case 290: /* windowdefn ::= nm AS window */
+ case 290: /* windowdefn ::= nm AS LP window RP */
{
- if( ALWAYS(yymsp[0].minor.yy147) ){
- yymsp[0].minor.yy147->zName = sqlite3DbStrNDup(pParse->db, yymsp[-2].minor.yy0.z, yymsp[-2].minor.yy0.n);
+ if( ALWAYS(yymsp[-1].minor.yy295) ){
+ yymsp[-1].minor.yy295->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n);
}
- yylhsminor.yy147 = yymsp[0].minor.yy147;
+ yylhsminor.yy295 = yymsp[-1].minor.yy295;
}
- yymsp[-2].minor.yy147 = yylhsminor.yy147;
+ yymsp[-4].minor.yy295 = yylhsminor.yy295;
break;
- case 291: /* window ::= LP part_opt orderby_opt frame_opt RP */
+ case 291: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */
{
- yymsp[-4].minor.yy147 = yymsp[-1].minor.yy147;
- if( ALWAYS(yymsp[-4].minor.yy147) ){
- yymsp[-4].minor.yy147->pPartition = yymsp[-3].minor.yy42;
- yymsp[-4].minor.yy147->pOrderBy = yymsp[-2].minor.yy42;
- }
+ yymsp[-4].minor.yy295 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy295, yymsp[-2].minor.yy434, yymsp[-1].minor.yy434, 0);
+}
+ break;
+ case 292: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
+{
+ yylhsminor.yy295 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy295, yymsp[-2].minor.yy434, yymsp[-1].minor.yy434, &yymsp[-5].minor.yy0);
+}
+ yymsp[-5].minor.yy295 = yylhsminor.yy295;
+ break;
+ case 293: /* window ::= ORDER BY sortlist frame_opt */
+{
+ yymsp[-3].minor.yy295 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy295, 0, yymsp[-1].minor.yy434, 0);
}
break;
- case 292: /* part_opt ::= PARTITION BY nexprlist */
-{ yymsp[-2].minor.yy42 = yymsp[0].minor.yy42; }
+ case 294: /* window ::= nm ORDER BY sortlist frame_opt */
+{
+ yylhsminor.yy295 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy295, 0, yymsp[-1].minor.yy434, &yymsp[-4].minor.yy0);
+}
+ yymsp[-4].minor.yy295 = yylhsminor.yy295;
+ break;
+ case 295: /* window ::= frame_opt */
+{
+ yylhsminor.yy295 = yymsp[0].minor.yy295;
+}
+ yymsp[0].minor.yy295 = yylhsminor.yy295;
break;
- case 293: /* part_opt ::= */
-{ yymsp[1].minor.yy42 = 0; }
+ case 296: /* window ::= nm frame_opt */
+{
+ yylhsminor.yy295 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy295, 0, 0, &yymsp[-1].minor.yy0);
+}
+ yymsp[-1].minor.yy295 = yylhsminor.yy295;
break;
- case 294: /* frame_opt ::= */
+ case 297: /* frame_opt ::= */
{
- yymsp[1].minor.yy147 = sqlite3WindowAlloc(pParse, TK_RANGE, TK_UNBOUNDED, 0, TK_CURRENT, 0);
+ yymsp[1].minor.yy295 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0);
}
break;
- case 295: /* frame_opt ::= range_or_rows frame_bound_s */
+ case 298: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
{
- yylhsminor.yy147 = sqlite3WindowAlloc(pParse, yymsp[-1].minor.yy96, yymsp[0].minor.yy317.eType, yymsp[0].minor.yy317.pExpr, TK_CURRENT, 0);
+ yylhsminor.yy295 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy494, yymsp[-1].minor.yy201.eType, yymsp[-1].minor.yy201.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy238);
}
- yymsp[-1].minor.yy147 = yylhsminor.yy147;
+ yymsp[-2].minor.yy295 = yylhsminor.yy295;
break;
- case 296: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e */
+ case 299: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
{
- yylhsminor.yy147 = sqlite3WindowAlloc(pParse, yymsp[-4].minor.yy96, yymsp[-2].minor.yy317.eType, yymsp[-2].minor.yy317.pExpr, yymsp[0].minor.yy317.eType, yymsp[0].minor.yy317.pExpr);
+ yylhsminor.yy295 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy494, yymsp[-3].minor.yy201.eType, yymsp[-3].minor.yy201.pExpr, yymsp[-1].minor.yy201.eType, yymsp[-1].minor.yy201.pExpr, yymsp[0].minor.yy238);
}
- yymsp[-4].minor.yy147 = yylhsminor.yy147;
+ yymsp[-5].minor.yy295 = yylhsminor.yy295;
break;
- case 297: /* range_or_rows ::= RANGE */
-{ yymsp[0].minor.yy96 = TK_RANGE; }
+ case 301: /* frame_bound_s ::= frame_bound */
+ case 303: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==303);
+{yylhsminor.yy201 = yymsp[0].minor.yy201;}
+ yymsp[0].minor.yy201 = yylhsminor.yy201;
break;
- case 298: /* range_or_rows ::= ROWS */
-{ yymsp[0].minor.yy96 = TK_ROWS; }
+ case 302: /* frame_bound_s ::= UNBOUNDED PRECEDING */
+ case 304: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==304);
+ case 306: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==306);
+{yylhsminor.yy201.eType = yymsp[-1].major; yylhsminor.yy201.pExpr = 0;}
+ yymsp[-1].minor.yy201 = yylhsminor.yy201;
break;
- case 299: /* frame_bound_s ::= frame_bound */
- case 301: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==301);
-{ yylhsminor.yy317 = yymsp[0].minor.yy317; }
- yymsp[0].minor.yy317 = yylhsminor.yy317;
+ case 305: /* frame_bound ::= expr PRECEDING|FOLLOWING */
+{yylhsminor.yy201.eType = yymsp[0].major; yylhsminor.yy201.pExpr = yymsp[-1].minor.yy524;}
+ yymsp[-1].minor.yy201 = yylhsminor.yy201;
break;
- case 300: /* frame_bound_s ::= UNBOUNDED PRECEDING */
- case 302: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==302);
-{yymsp[-1].minor.yy317.eType = TK_UNBOUNDED; yymsp[-1].minor.yy317.pExpr = 0;}
+ case 307: /* frame_exclude_opt ::= */
+{yymsp[1].minor.yy238 = 0;}
break;
- case 303: /* frame_bound ::= expr PRECEDING */
-{ yylhsminor.yy317.eType = TK_PRECEDING; yylhsminor.yy317.pExpr = yymsp[-1].minor.yy490; }
- yymsp[-1].minor.yy317 = yylhsminor.yy317;
+ case 308: /* frame_exclude_opt ::= EXCLUDE frame_exclude */
+{yymsp[-1].minor.yy238 = yymsp[0].minor.yy238;}
break;
- case 304: /* frame_bound ::= CURRENT ROW */
-{ yymsp[-1].minor.yy317.eType = TK_CURRENT ; yymsp[-1].minor.yy317.pExpr = 0; }
+ case 309: /* frame_exclude ::= NO OTHERS */
+ case 310: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==310);
+{yymsp[-1].minor.yy238 = yymsp[-1].major; /*A-overwrites-X*/}
break;
- case 305: /* frame_bound ::= expr FOLLOWING */
-{ yylhsminor.yy317.eType = TK_FOLLOWING; yylhsminor.yy317.pExpr = yymsp[-1].minor.yy490; }
- yymsp[-1].minor.yy317 = yylhsminor.yy317;
+ case 311: /* frame_exclude ::= GROUP|TIES */
+{yymsp[0].minor.yy238 = yymsp[0].major; /*A-overwrites-X*/}
break;
- case 306: /* window_clause ::= WINDOW windowdefn_list */
-{ yymsp[-1].minor.yy147 = yymsp[0].minor.yy147; }
+ case 312: /* window_clause ::= WINDOW windowdefn_list */
+{ yymsp[-1].minor.yy295 = yymsp[0].minor.yy295; }
break;
- case 307: /* over_clause ::= filter_opt OVER window */
+ case 313: /* over_clause ::= filter_opt OVER LP window RP */
{
- yylhsminor.yy147 = yymsp[0].minor.yy147;
- assert( yylhsminor.yy147!=0 );
- yylhsminor.yy147->pFilter = yymsp[-2].minor.yy490;
+ yylhsminor.yy295 = yymsp[-1].minor.yy295;
+ assert( yylhsminor.yy295!=0 );
+ yylhsminor.yy295->pFilter = yymsp[-4].minor.yy524;
}
- yymsp[-2].minor.yy147 = yylhsminor.yy147;
+ yymsp[-4].minor.yy295 = yylhsminor.yy295;
break;
- case 308: /* over_clause ::= filter_opt OVER nm */
+ case 314: /* over_clause ::= filter_opt OVER nm */
{
- yylhsminor.yy147 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
- if( yylhsminor.yy147 ){
- yylhsminor.yy147->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n);
- yylhsminor.yy147->pFilter = yymsp[-2].minor.yy490;
+ yylhsminor.yy295 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
+ if( yylhsminor.yy295 ){
+ yylhsminor.yy295->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n);
+ yylhsminor.yy295->pFilter = yymsp[-2].minor.yy524;
}else{
- sqlite3ExprDelete(pParse->db, yymsp[-2].minor.yy490);
+ sqlite3ExprDelete(pParse->db, yymsp[-2].minor.yy524);
}
}
- yymsp[-2].minor.yy147 = yylhsminor.yy147;
+ yymsp[-2].minor.yy295 = yylhsminor.yy295;
break;
- case 310: /* filter_opt ::= FILTER LP WHERE expr RP */
-{ yymsp[-4].minor.yy490 = yymsp[-1].minor.yy490; }
+ case 316: /* filter_opt ::= FILTER LP WHERE expr RP */
+{ yymsp[-4].minor.yy524 = yymsp[-1].minor.yy524; }
break;
default:
- /* (311) input ::= cmdlist */ yytestcase(yyruleno==311);
- /* (312) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==312);
- /* (313) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=313);
- /* (314) ecmd ::= SEMI */ yytestcase(yyruleno==314);
- /* (315) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==315);
- /* (316) ecmd ::= explain cmdx */ yytestcase(yyruleno==316);
- /* (317) trans_opt ::= */ yytestcase(yyruleno==317);
- /* (318) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==318);
- /* (319) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==319);
- /* (320) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==320);
- /* (321) savepoint_opt ::= */ yytestcase(yyruleno==321);
- /* (322) cmd ::= create_table create_table_args */ yytestcase(yyruleno==322);
- /* (323) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==323);
- /* (324) columnlist ::= columnname carglist */ yytestcase(yyruleno==324);
- /* (325) nm ::= ID|INDEXED */ yytestcase(yyruleno==325);
- /* (326) nm ::= STRING */ yytestcase(yyruleno==326);
- /* (327) nm ::= JOIN_KW */ yytestcase(yyruleno==327);
- /* (328) typetoken ::= typename */ yytestcase(yyruleno==328);
- /* (329) typename ::= ID|STRING */ yytestcase(yyruleno==329);
- /* (330) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=330);
- /* (331) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=331);
- /* (332) carglist ::= carglist ccons */ yytestcase(yyruleno==332);
- /* (333) carglist ::= */ yytestcase(yyruleno==333);
- /* (334) ccons ::= NULL onconf */ yytestcase(yyruleno==334);
- /* (335) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==335);
- /* (336) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==336);
- /* (337) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=337);
- /* (338) tconscomma ::= */ yytestcase(yyruleno==338);
- /* (339) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=339);
- /* (340) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=340);
- /* (341) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=341);
- /* (342) oneselect ::= values */ yytestcase(yyruleno==342);
- /* (343) sclp ::= selcollist COMMA */ yytestcase(yyruleno==343);
- /* (344) as ::= ID|STRING */ yytestcase(yyruleno==344);
- /* (345) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=345);
- /* (346) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==346);
- /* (347) exprlist ::= nexprlist */ yytestcase(yyruleno==347);
- /* (348) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=348);
- /* (349) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=349);
- /* (350) nmnum ::= ON */ yytestcase(yyruleno==350);
- /* (351) nmnum ::= DELETE */ yytestcase(yyruleno==351);
- /* (352) nmnum ::= DEFAULT */ yytestcase(yyruleno==352);
- /* (353) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==353);
- /* (354) foreach_clause ::= */ yytestcase(yyruleno==354);
- /* (355) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==355);
- /* (356) trnm ::= nm */ yytestcase(yyruleno==356);
- /* (357) tridxby ::= */ yytestcase(yyruleno==357);
- /* (358) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==358);
- /* (359) database_kw_opt ::= */ yytestcase(yyruleno==359);
- /* (360) kwcolumn_opt ::= */ yytestcase(yyruleno==360);
- /* (361) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==361);
- /* (362) vtabarglist ::= vtabarg */ yytestcase(yyruleno==362);
- /* (363) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==363);
- /* (364) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==364);
- /* (365) anylist ::= */ yytestcase(yyruleno==365);
- /* (366) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==366);
- /* (367) anylist ::= anylist ANY */ yytestcase(yyruleno==367);
- /* (368) with ::= */ yytestcase(yyruleno==368);
+ /* (317) input ::= cmdlist */ yytestcase(yyruleno==317);
+ /* (318) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==318);
+ /* (319) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=319);
+ /* (320) ecmd ::= SEMI */ yytestcase(yyruleno==320);
+ /* (321) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==321);
+ /* (322) ecmd ::= explain cmdx */ yytestcase(yyruleno==322);
+ /* (323) trans_opt ::= */ yytestcase(yyruleno==323);
+ /* (324) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==324);
+ /* (325) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==325);
+ /* (326) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==326);
+ /* (327) savepoint_opt ::= */ yytestcase(yyruleno==327);
+ /* (328) cmd ::= create_table create_table_args */ yytestcase(yyruleno==328);
+ /* (329) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==329);
+ /* (330) columnlist ::= columnname carglist */ yytestcase(yyruleno==330);
+ /* (331) nm ::= ID|INDEXED */ yytestcase(yyruleno==331);
+ /* (332) nm ::= STRING */ yytestcase(yyruleno==332);
+ /* (333) nm ::= JOIN_KW */ yytestcase(yyruleno==333);
+ /* (334) typetoken ::= typename */ yytestcase(yyruleno==334);
+ /* (335) typename ::= ID|STRING */ yytestcase(yyruleno==335);
+ /* (336) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=336);
+ /* (337) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=337);
+ /* (338) carglist ::= carglist ccons */ yytestcase(yyruleno==338);
+ /* (339) carglist ::= */ yytestcase(yyruleno==339);
+ /* (340) ccons ::= NULL onconf */ yytestcase(yyruleno==340);
+ /* (341) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==341);
+ /* (342) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==342);
+ /* (343) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=343);
+ /* (344) tconscomma ::= */ yytestcase(yyruleno==344);
+ /* (345) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=345);
+ /* (346) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=346);
+ /* (347) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=347);
+ /* (348) oneselect ::= values */ yytestcase(yyruleno==348);
+ /* (349) sclp ::= selcollist COMMA */ yytestcase(yyruleno==349);
+ /* (350) as ::= ID|STRING */ yytestcase(yyruleno==350);
+ /* (351) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=351);
+ /* (352) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==352);
+ /* (353) exprlist ::= nexprlist */ yytestcase(yyruleno==353);
+ /* (354) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=354);
+ /* (355) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=355);
+ /* (356) nmnum ::= ON */ yytestcase(yyruleno==356);
+ /* (357) nmnum ::= DELETE */ yytestcase(yyruleno==357);
+ /* (358) nmnum ::= DEFAULT */ yytestcase(yyruleno==358);
+ /* (359) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==359);
+ /* (360) foreach_clause ::= */ yytestcase(yyruleno==360);
+ /* (361) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==361);
+ /* (362) trnm ::= nm */ yytestcase(yyruleno==362);
+ /* (363) tridxby ::= */ yytestcase(yyruleno==363);
+ /* (364) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==364);
+ /* (365) database_kw_opt ::= */ yytestcase(yyruleno==365);
+ /* (366) kwcolumn_opt ::= */ yytestcase(yyruleno==366);
+ /* (367) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==367);
+ /* (368) vtabarglist ::= vtabarg */ yytestcase(yyruleno==368);
+ /* (369) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==369);
+ /* (370) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==370);
+ /* (371) anylist ::= */ yytestcase(yyruleno==371);
+ /* (372) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==372);
+ /* (373) anylist ::= anylist ANY */ yytestcase(yyruleno==373);
+ /* (374) with ::= */ yytestcase(yyruleno==374);
break;
/********** End reduce actions ************************************************/
};
@@ -152276,144 +153181,144 @@ const unsigned char ebcdicToAscii[] = {
** is substantially reduced. This is important for embedded applications
** on platforms with limited memory.
*/
-/* Hash score: 208 */
-/* zKWText[] encodes 923 bytes of keyword text in 614 bytes */
+/* Hash score: 214 */
+/* zKWText[] encodes 950 bytes of keyword text in 629 bytes */
/* REINDEXEDESCAPEACHECKEYBEFOREIGNOREGEXPLAINSTEADDATABASELECT */
-/* ABLEFTHENDEFERRABLELSEXCEPTRANSACTIONATURALTERAISEXCLUSIVE */
-/* XISTSAVEPOINTERSECTRIGGEREFERENCESCONSTRAINTOFFSETEMPORARY */
-/* UNIQUERYWITHOUTERELEASEATTACHAVINGROUPDATEBEGINNERANGEBETWEEN */
-/* OTHINGLOBYCASCADELETECASECOLLATECREATECURRENT_DATEDETACH */
-/* IMMEDIATEJOINSERTLIKEMATCHPLANALYZEPRAGMABORTVALUESVIRTUALIMIT */
-/* WHENOTNULLWHERECURSIVEAFTERENAMEANDEFAULTAUTOINCREMENTCAST */
-/* COLUMNCOMMITCONFLICTCROSSCURRENT_TIMESTAMPARTITIONDEFERRED */
-/* ISTINCTDROPRECEDINGFAILFILTEREPLACEFOLLOWINGFROMFULLIFISNULL */
-/* ORDERESTRICTOVERIGHTROLLBACKROWSUNBOUNDEDUNIONUSINGVACUUMVIEW */
-/* INDOWINITIALLYPRIMARY */
-static const char zKWText[613] = {
+/* ABLEFTHENDEFERRABLELSEXCLUDELETEMPORARYCONSTRAINTERSECTIES */
+/* AVEPOINTOFFSETRANSACTIONATURALTERAISEXCEPTRIGGEREFERENCES */
+/* UNIQUERYWITHOUTERELEASEXCLUSIVEXISTSATTACHAVINGLOBEGINNERANGE */
+/* BETWEENOTHINGROUPSCASCADETACHCASECOLLATECREATECURRENT_DATE */
+/* IMMEDIATEJOINSERTLIKEMATCHPLANALYZEPRAGMABORTUPDATEVALUES */
+/* VIRTUALIMITWHENOTNULLWHERECURSIVEAFTERENAMEANDEFAULT */
+/* AUTOINCREMENTCASTCOLUMNCOMMITCONFLICTCROSSCURRENT_TIMESTAMP */
+/* ARTITIONDEFERREDISTINCTDROPRECEDINGFAILFILTEREPLACEFOLLOWING */
+/* FROMFULLIFISNULLORDERESTRICTOTHERSOVERIGHTROLLBACKROWS */
+/* UNBOUNDEDUNIONUSINGVACUUMVIEWINDOWBYINITIALLYPRIMARY */
+static const char zKWText[628] = {
'R','E','I','N','D','E','X','E','D','E','S','C','A','P','E','A','C','H',
'E','C','K','E','Y','B','E','F','O','R','E','I','G','N','O','R','E','G',
'E','X','P','L','A','I','N','S','T','E','A','D','D','A','T','A','B','A',
'S','E','L','E','C','T','A','B','L','E','F','T','H','E','N','D','E','F',
- 'E','R','R','A','B','L','E','L','S','E','X','C','E','P','T','R','A','N',
- 'S','A','C','T','I','O','N','A','T','U','R','A','L','T','E','R','A','I',
- 'S','E','X','C','L','U','S','I','V','E','X','I','S','T','S','A','V','E',
- 'P','O','I','N','T','E','R','S','E','C','T','R','I','G','G','E','R','E',
- 'F','E','R','E','N','C','E','S','C','O','N','S','T','R','A','I','N','T',
- 'O','F','F','S','E','T','E','M','P','O','R','A','R','Y','U','N','I','Q',
- 'U','E','R','Y','W','I','T','H','O','U','T','E','R','E','L','E','A','S',
- 'E','A','T','T','A','C','H','A','V','I','N','G','R','O','U','P','D','A',
- 'T','E','B','E','G','I','N','N','E','R','A','N','G','E','B','E','T','W',
- 'E','E','N','O','T','H','I','N','G','L','O','B','Y','C','A','S','C','A',
- 'D','E','L','E','T','E','C','A','S','E','C','O','L','L','A','T','E','C',
- 'R','E','A','T','E','C','U','R','R','E','N','T','_','D','A','T','E','D',
- 'E','T','A','C','H','I','M','M','E','D','I','A','T','E','J','O','I','N',
- 'S','E','R','T','L','I','K','E','M','A','T','C','H','P','L','A','N','A',
- 'L','Y','Z','E','P','R','A','G','M','A','B','O','R','T','V','A','L','U',
- 'E','S','V','I','R','T','U','A','L','I','M','I','T','W','H','E','N','O',
- 'T','N','U','L','L','W','H','E','R','E','C','U','R','S','I','V','E','A',
- 'F','T','E','R','E','N','A','M','E','A','N','D','E','F','A','U','L','T',
- 'A','U','T','O','I','N','C','R','E','M','E','N','T','C','A','S','T','C',
- 'O','L','U','M','N','C','O','M','M','I','T','C','O','N','F','L','I','C',
- 'T','C','R','O','S','S','C','U','R','R','E','N','T','_','T','I','M','E',
- 'S','T','A','M','P','A','R','T','I','T','I','O','N','D','E','F','E','R',
- 'R','E','D','I','S','T','I','N','C','T','D','R','O','P','R','E','C','E',
- 'D','I','N','G','F','A','I','L','F','I','L','T','E','R','E','P','L','A',
- 'C','E','F','O','L','L','O','W','I','N','G','F','R','O','M','F','U','L',
- 'L','I','F','I','S','N','U','L','L','O','R','D','E','R','E','S','T','R',
- 'I','C','T','O','V','E','R','I','G','H','T','R','O','L','L','B','A','C',
- 'K','R','O','W','S','U','N','B','O','U','N','D','E','D','U','N','I','O',
- 'N','U','S','I','N','G','V','A','C','U','U','M','V','I','E','W','I','N',
- 'D','O','W','I','N','I','T','I','A','L','L','Y','P','R','I','M','A','R',
- 'Y',
+ 'E','R','R','A','B','L','E','L','S','E','X','C','L','U','D','E','L','E',
+ 'T','E','M','P','O','R','A','R','Y','C','O','N','S','T','R','A','I','N',
+ 'T','E','R','S','E','C','T','I','E','S','A','V','E','P','O','I','N','T',
+ 'O','F','F','S','E','T','R','A','N','S','A','C','T','I','O','N','A','T',
+ 'U','R','A','L','T','E','R','A','I','S','E','X','C','E','P','T','R','I',
+ 'G','G','E','R','E','F','E','R','E','N','C','E','S','U','N','I','Q','U',
+ 'E','R','Y','W','I','T','H','O','U','T','E','R','E','L','E','A','S','E',
+ 'X','C','L','U','S','I','V','E','X','I','S','T','S','A','T','T','A','C',
+ 'H','A','V','I','N','G','L','O','B','E','G','I','N','N','E','R','A','N',
+ 'G','E','B','E','T','W','E','E','N','O','T','H','I','N','G','R','O','U',
+ 'P','S','C','A','S','C','A','D','E','T','A','C','H','C','A','S','E','C',
+ 'O','L','L','A','T','E','C','R','E','A','T','E','C','U','R','R','E','N',
+ 'T','_','D','A','T','E','I','M','M','E','D','I','A','T','E','J','O','I',
+ 'N','S','E','R','T','L','I','K','E','M','A','T','C','H','P','L','A','N',
+ 'A','L','Y','Z','E','P','R','A','G','M','A','B','O','R','T','U','P','D',
+ 'A','T','E','V','A','L','U','E','S','V','I','R','T','U','A','L','I','M',
+ 'I','T','W','H','E','N','O','T','N','U','L','L','W','H','E','R','E','C',
+ 'U','R','S','I','V','E','A','F','T','E','R','E','N','A','M','E','A','N',
+ 'D','E','F','A','U','L','T','A','U','T','O','I','N','C','R','E','M','E',
+ 'N','T','C','A','S','T','C','O','L','U','M','N','C','O','M','M','I','T',
+ 'C','O','N','F','L','I','C','T','C','R','O','S','S','C','U','R','R','E',
+ 'N','T','_','T','I','M','E','S','T','A','M','P','A','R','T','I','T','I',
+ 'O','N','D','E','F','E','R','R','E','D','I','S','T','I','N','C','T','D',
+ 'R','O','P','R','E','C','E','D','I','N','G','F','A','I','L','F','I','L',
+ 'T','E','R','E','P','L','A','C','E','F','O','L','L','O','W','I','N','G',
+ 'F','R','O','M','F','U','L','L','I','F','I','S','N','U','L','L','O','R',
+ 'D','E','R','E','S','T','R','I','C','T','O','T','H','E','R','S','O','V',
+ 'E','R','I','G','H','T','R','O','L','L','B','A','C','K','R','O','W','S',
+ 'U','N','B','O','U','N','D','E','D','U','N','I','O','N','U','S','I','N',
+ 'G','V','A','C','U','U','M','V','I','E','W','I','N','D','O','W','B','Y',
+ 'I','N','I','T','I','A','L','L','Y','P','R','I','M','A','R','Y',
};
/* aKWHash[i] is the hash value for the i-th keyword */
static const unsigned char aKWHash[127] = {
- 74, 109, 124, 72, 106, 45, 0, 0, 81, 0, 76, 61, 0,
- 42, 12, 77, 15, 0, 123, 84, 54, 118, 125, 19, 0, 0,
- 130, 0, 128, 121, 0, 22, 96, 0, 9, 0, 0, 115, 69,
- 0, 67, 6, 0, 48, 93, 136, 0, 126, 104, 0, 0, 44,
- 0, 107, 24, 0, 17, 0, 131, 53, 23, 0, 5, 62, 132,
- 99, 0, 0, 135, 110, 60, 134, 57, 113, 55, 0, 94, 0,
- 103, 26, 0, 102, 0, 0, 0, 98, 95, 100, 105, 117, 14,
- 39, 116, 0, 80, 0, 133, 114, 92, 59, 0, 129, 79, 119,
- 86, 46, 83, 0, 0, 97, 40, 122, 120, 0, 127, 0, 0,
- 29, 0, 89, 87, 88, 0, 20, 85, 111, 56,
+ 75, 111, 127, 73, 108, 29, 0, 0, 83, 0, 77, 63, 0,
+ 37, 33, 78, 15, 0, 126, 86, 57, 120, 128, 19, 0, 0,
+ 133, 0, 131, 123, 0, 22, 98, 0, 9, 0, 0, 117, 71,
+ 0, 69, 6, 0, 49, 95, 140, 0, 129, 106, 0, 0, 54,
+ 0, 109, 24, 0, 17, 0, 134, 56, 23, 26, 5, 58, 135,
+ 101, 0, 0, 139, 112, 62, 138, 59, 115, 65, 0, 96, 0,
+ 105, 45, 0, 104, 0, 0, 0, 100, 97, 102, 107, 119, 14,
+ 31, 118, 0, 81, 0, 136, 116, 137, 61, 124, 132, 80, 121,
+ 88, 30, 85, 0, 0, 99, 35, 125, 122, 0, 130, 0, 0,
+ 41, 0, 91, 89, 90, 0, 20, 87, 113, 82,
};
/* aKWNext[] forms the hash collision chain. If aKWHash[i]==0
** then the i-th keyword has no more hash collisions. Otherwise,
** the next keyword with the same hash is aKWHash[i]-1. */
-static const unsigned char aKWNext[136] = {
+static const unsigned char aKWNext[140] = {
0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0,
0, 2, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0,
- 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 33, 0, 21, 0, 0, 0, 0, 0, 50,
- 0, 43, 3, 47, 0, 0, 32, 0, 0, 0, 0, 0, 0,
- 0, 1, 64, 0, 0, 65, 0, 41, 0, 38, 0, 0, 0,
- 0, 0, 49, 75, 0, 0, 30, 0, 58, 0, 0, 0, 31,
- 63, 16, 34, 10, 0, 0, 0, 0, 0, 0, 0, 11, 70,
- 91, 0, 0, 8, 0, 108, 0, 101, 28, 52, 68, 0, 112,
- 0, 73, 51, 0, 90, 27, 37, 0, 71, 36, 82, 0, 35,
- 66, 25, 18, 0, 0, 78,
+ 0, 0, 0, 21, 0, 0, 12, 0, 0, 0, 0, 0, 0,
+ 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 51, 28, 0, 0, 38, 0, 0, 0, 44, 0, 0, 0, 3,
+ 0, 0, 67, 1, 66, 0, 0, 0, 36, 0, 47, 0, 0,
+ 0, 0, 0, 48, 50, 76, 0, 0, 42, 0, 60, 0, 0,
+ 0, 43, 0, 16, 55, 10, 0, 0, 0, 0, 0, 0, 0,
+ 11, 72, 93, 0, 0, 8, 0, 110, 0, 103, 40, 53, 70,
+ 0, 114, 0, 74, 52, 0, 0, 92, 39, 46, 0, 68, 32,
+ 84, 0, 34, 27, 25, 18, 94, 0, 64, 79,
};
/* aKWLen[i] is the length (in bytes) of the i-th keyword */
-static const unsigned char aKWLen[136] = {
+static const unsigned char aKWLen[140] = {
7, 7, 5, 4, 6, 4, 5, 3, 6, 7, 3, 6, 6,
- 7, 7, 3, 8, 2, 6, 5, 4, 4, 3, 10, 4, 6,
- 11, 6, 2, 7, 5, 5, 9, 6, 9, 9, 7, 10, 10,
- 4, 6, 2, 3, 9, 4, 2, 6, 5, 7, 4, 5, 7,
- 6, 6, 5, 6, 5, 5, 5, 7, 7, 4, 2, 7, 3,
- 6, 4, 7, 6, 12, 6, 9, 4, 6, 4, 5, 4, 7,
- 6, 5, 6, 7, 5, 4, 7, 3, 2, 4, 5, 9, 5,
- 6, 3, 7, 13, 2, 2, 4, 6, 6, 8, 5, 17, 12,
- 7, 9, 8, 8, 2, 4, 9, 4, 6, 7, 9, 4, 4,
- 2, 6, 5, 8, 4, 5, 8, 4, 3, 9, 5, 5, 6,
- 4, 6, 2, 9, 3, 7,
+ 7, 7, 3, 8, 2, 6, 5, 4, 4, 3, 10, 4, 7,
+ 6, 9, 4, 2, 10, 9, 4, 9, 4, 6, 2, 3, 11,
+ 6, 2, 7, 5, 5, 6, 7, 10, 6, 5, 7, 4, 5,
+ 7, 9, 6, 6, 6, 4, 5, 5, 5, 7, 7, 6, 5,
+ 7, 3, 6, 4, 7, 6, 12, 9, 4, 6, 4, 5, 4,
+ 7, 6, 5, 6, 6, 7, 5, 4, 7, 3, 2, 4, 5,
+ 9, 5, 6, 3, 7, 13, 2, 2, 4, 6, 6, 8, 5,
+ 17, 12, 7, 9, 8, 8, 2, 4, 9, 4, 6, 7, 9,
+ 4, 4, 2, 6, 5, 8, 6, 4, 5, 8, 4, 3, 9,
+ 5, 5, 6, 4, 6, 2, 2, 9, 3, 7,
};
/* aKWOffset[i] is the index into zKWText[] of the start of
** the text for the i-th keyword. */
-static const unsigned short int aKWOffset[136] = {
+static const unsigned short int aKWOffset[140] = {
0, 2, 2, 8, 9, 14, 16, 20, 23, 25, 25, 29, 33,
36, 41, 46, 48, 53, 54, 59, 62, 65, 67, 69, 78, 81,
- 86, 91, 95, 96, 101, 105, 109, 117, 122, 128, 136, 142, 152,
- 159, 162, 162, 165, 167, 167, 171, 176, 179, 184, 184, 188, 192,
- 199, 204, 209, 212, 218, 221, 225, 230, 236, 242, 245, 247, 248,
- 252, 258, 262, 269, 275, 287, 293, 302, 304, 310, 314, 319, 321,
- 328, 333, 338, 344, 350, 355, 358, 358, 358, 361, 365, 368, 377,
- 381, 387, 389, 396, 398, 400, 409, 413, 419, 425, 433, 438, 438,
- 438, 454, 463, 470, 471, 478, 481, 490, 494, 499, 506, 515, 519,
- 523, 525, 531, 535, 543, 546, 551, 559, 559, 563, 572, 577, 582,
- 588, 591, 594, 597, 602, 606,
+ 86, 90, 90, 94, 99, 106, 114, 117, 123, 126, 126, 129, 131,
+ 136, 140, 141, 146, 150, 154, 159, 165, 175, 178, 183, 183, 187,
+ 191, 197, 205, 211, 216, 221, 224, 227, 231, 236, 242, 248, 248,
+ 254, 255, 259, 265, 269, 276, 282, 294, 303, 305, 311, 315, 320,
+ 322, 329, 334, 339, 345, 351, 357, 362, 365, 365, 365, 368, 372,
+ 375, 384, 388, 394, 396, 403, 405, 407, 416, 420, 426, 432, 440,
+ 445, 445, 445, 461, 470, 477, 478, 485, 488, 497, 501, 506, 513,
+ 522, 526, 530, 532, 538, 542, 550, 556, 559, 564, 572, 572, 576,
+ 585, 590, 595, 601, 604, 607, 610, 612, 617, 621,
};
/* aKWCode[i] is the parser symbol code for the i-th keyword */
-static const unsigned char aKWCode[136] = {
+static const unsigned char aKWCode[140] = {
TK_REINDEX, TK_INDEXED, TK_INDEX, TK_DESC, TK_ESCAPE,
TK_EACH, TK_CHECK, TK_KEY, TK_BEFORE, TK_FOREIGN,
TK_FOR, TK_IGNORE, TK_LIKE_KW, TK_EXPLAIN, TK_INSTEAD,
TK_ADD, TK_DATABASE, TK_AS, TK_SELECT, TK_TABLE,
TK_JOIN_KW, TK_THEN, TK_END, TK_DEFERRABLE, TK_ELSE,
- TK_EXCEPT, TK_TRANSACTION,TK_ACTION, TK_ON, TK_JOIN_KW,
- TK_ALTER, TK_RAISE, TK_EXCLUSIVE, TK_EXISTS, TK_SAVEPOINT,
- TK_INTERSECT, TK_TRIGGER, TK_REFERENCES, TK_CONSTRAINT, TK_INTO,
- TK_OFFSET, TK_OF, TK_SET, TK_TEMP, TK_TEMP,
- TK_OR, TK_UNIQUE, TK_QUERY, TK_WITHOUT, TK_WITH,
- TK_JOIN_KW, TK_RELEASE, TK_ATTACH, TK_HAVING, TK_GROUP,
- TK_UPDATE, TK_BEGIN, TK_JOIN_KW, TK_RANGE, TK_BETWEEN,
- TK_NOTHING, TK_LIKE_KW, TK_BY, TK_CASCADE, TK_ASC,
- TK_DELETE, TK_CASE, TK_COLLATE, TK_CREATE, TK_CTIME_KW,
- TK_DETACH, TK_IMMEDIATE, TK_JOIN, TK_INSERT, TK_LIKE_KW,
- TK_MATCH, TK_PLAN, TK_ANALYZE, TK_PRAGMA, TK_ABORT,
- TK_VALUES, TK_VIRTUAL, TK_LIMIT, TK_WHEN, TK_NOTNULL,
- TK_NOT, TK_NO, TK_NULL, TK_WHERE, TK_RECURSIVE,
- TK_AFTER, TK_RENAME, TK_AND, TK_DEFAULT, TK_AUTOINCR,
- TK_TO, TK_IN, TK_CAST, TK_COLUMNKW, TK_COMMIT,
- TK_CONFLICT, TK_JOIN_KW, TK_CTIME_KW, TK_CTIME_KW, TK_CURRENT,
- TK_PARTITION, TK_DEFERRED, TK_DISTINCT, TK_IS, TK_DROP,
- TK_PRECEDING, TK_FAIL, TK_FILTER, TK_REPLACE, TK_FOLLOWING,
- TK_FROM, TK_JOIN_KW, TK_IF, TK_ISNULL, TK_ORDER,
- TK_RESTRICT, TK_OVER, TK_JOIN_KW, TK_ROLLBACK, TK_ROWS,
- TK_ROW, TK_UNBOUNDED, TK_UNION, TK_USING, TK_VACUUM,
- TK_VIEW, TK_WINDOW, TK_DO, TK_INITIALLY, TK_ALL,
- TK_PRIMARY,
+ TK_EXCLUDE, TK_DELETE, TK_TEMP, TK_TEMP, TK_OR,
+ TK_CONSTRAINT, TK_INTERSECT, TK_TIES, TK_SAVEPOINT, TK_INTO,
+ TK_OFFSET, TK_OF, TK_SET, TK_TRANSACTION,TK_ACTION,
+ TK_ON, TK_JOIN_KW, TK_ALTER, TK_RAISE, TK_EXCEPT,
+ TK_TRIGGER, TK_REFERENCES, TK_UNIQUE, TK_QUERY, TK_WITHOUT,
+ TK_WITH, TK_JOIN_KW, TK_RELEASE, TK_EXCLUSIVE, TK_EXISTS,
+ TK_ATTACH, TK_HAVING, TK_LIKE_KW, TK_BEGIN, TK_JOIN_KW,
+ TK_RANGE, TK_BETWEEN, TK_NOTHING, TK_GROUPS, TK_GROUP,
+ TK_CASCADE, TK_ASC, TK_DETACH, TK_CASE, TK_COLLATE,
+ TK_CREATE, TK_CTIME_KW, TK_IMMEDIATE, TK_JOIN, TK_INSERT,
+ TK_LIKE_KW, TK_MATCH, TK_PLAN, TK_ANALYZE, TK_PRAGMA,
+ TK_ABORT, TK_UPDATE, TK_VALUES, TK_VIRTUAL, TK_LIMIT,
+ TK_WHEN, TK_NOTNULL, TK_NOT, TK_NO, TK_NULL,
+ TK_WHERE, TK_RECURSIVE, TK_AFTER, TK_RENAME, TK_AND,
+ TK_DEFAULT, TK_AUTOINCR, TK_TO, TK_IN, TK_CAST,
+ TK_COLUMNKW, TK_COMMIT, TK_CONFLICT, TK_JOIN_KW, TK_CTIME_KW,
+ TK_CTIME_KW, TK_CURRENT, TK_PARTITION, TK_DEFERRED, TK_DISTINCT,
+ TK_IS, TK_DROP, TK_PRECEDING, TK_FAIL, TK_FILTER,
+ TK_REPLACE, TK_FOLLOWING, TK_FROM, TK_JOIN_KW, TK_IF,
+ TK_ISNULL, TK_ORDER, TK_RESTRICT, TK_OTHERS, TK_OVER,
+ TK_JOIN_KW, TK_ROLLBACK, TK_ROWS, TK_ROW, TK_UNBOUNDED,
+ TK_UNION, TK_USING, TK_VACUUM, TK_VIEW, TK_WINDOW,
+ TK_DO, TK_BY, TK_INITIALLY, TK_ALL, TK_PRIMARY,
};
/* Check to see if z[0..n-1] is a keyword. If it is, write the
** parser symbol code for that keyword into *pType. Always
@@ -152459,117 +153364,121 @@ static int keywordCode(const char *z, int n, int *pType){
testcase( i==22 ); /* END */
testcase( i==23 ); /* DEFERRABLE */
testcase( i==24 ); /* ELSE */
- testcase( i==25 ); /* EXCEPT */
- testcase( i==26 ); /* TRANSACTION */
- testcase( i==27 ); /* ACTION */
- testcase( i==28 ); /* ON */
- testcase( i==29 ); /* NATURAL */
- testcase( i==30 ); /* ALTER */
- testcase( i==31 ); /* RAISE */
- testcase( i==32 ); /* EXCLUSIVE */
- testcase( i==33 ); /* EXISTS */
- testcase( i==34 ); /* SAVEPOINT */
- testcase( i==35 ); /* INTERSECT */
- testcase( i==36 ); /* TRIGGER */
- testcase( i==37 ); /* REFERENCES */
- testcase( i==38 ); /* CONSTRAINT */
- testcase( i==39 ); /* INTO */
- testcase( i==40 ); /* OFFSET */
- testcase( i==41 ); /* OF */
- testcase( i==42 ); /* SET */
- testcase( i==43 ); /* TEMPORARY */
- testcase( i==44 ); /* TEMP */
- testcase( i==45 ); /* OR */
- testcase( i==46 ); /* UNIQUE */
- testcase( i==47 ); /* QUERY */
- testcase( i==48 ); /* WITHOUT */
- testcase( i==49 ); /* WITH */
- testcase( i==50 ); /* OUTER */
- testcase( i==51 ); /* RELEASE */
- testcase( i==52 ); /* ATTACH */
- testcase( i==53 ); /* HAVING */
- testcase( i==54 ); /* GROUP */
- testcase( i==55 ); /* UPDATE */
- testcase( i==56 ); /* BEGIN */
- testcase( i==57 ); /* INNER */
- testcase( i==58 ); /* RANGE */
- testcase( i==59 ); /* BETWEEN */
- testcase( i==60 ); /* NOTHING */
- testcase( i==61 ); /* GLOB */
- testcase( i==62 ); /* BY */
- testcase( i==63 ); /* CASCADE */
- testcase( i==64 ); /* ASC */
- testcase( i==65 ); /* DELETE */
- testcase( i==66 ); /* CASE */
- testcase( i==67 ); /* COLLATE */
- testcase( i==68 ); /* CREATE */
- testcase( i==69 ); /* CURRENT_DATE */
- testcase( i==70 ); /* DETACH */
- testcase( i==71 ); /* IMMEDIATE */
- testcase( i==72 ); /* JOIN */
- testcase( i==73 ); /* INSERT */
- testcase( i==74 ); /* LIKE */
- testcase( i==75 ); /* MATCH */
- testcase( i==76 ); /* PLAN */
- testcase( i==77 ); /* ANALYZE */
- testcase( i==78 ); /* PRAGMA */
- testcase( i==79 ); /* ABORT */
- testcase( i==80 ); /* VALUES */
- testcase( i==81 ); /* VIRTUAL */
- testcase( i==82 ); /* LIMIT */
- testcase( i==83 ); /* WHEN */
- testcase( i==84 ); /* NOTNULL */
- testcase( i==85 ); /* NOT */
- testcase( i==86 ); /* NO */
- testcase( i==87 ); /* NULL */
- testcase( i==88 ); /* WHERE */
- testcase( i==89 ); /* RECURSIVE */
- testcase( i==90 ); /* AFTER */
- testcase( i==91 ); /* RENAME */
- testcase( i==92 ); /* AND */
- testcase( i==93 ); /* DEFAULT */
- testcase( i==94 ); /* AUTOINCREMENT */
- testcase( i==95 ); /* TO */
- testcase( i==96 ); /* IN */
- testcase( i==97 ); /* CAST */
- testcase( i==98 ); /* COLUMN */
- testcase( i==99 ); /* COMMIT */
- testcase( i==100 ); /* CONFLICT */
- testcase( i==101 ); /* CROSS */
- testcase( i==102 ); /* CURRENT_TIMESTAMP */
- testcase( i==103 ); /* CURRENT_TIME */
- testcase( i==104 ); /* CURRENT */
- testcase( i==105 ); /* PARTITION */
- testcase( i==106 ); /* DEFERRED */
- testcase( i==107 ); /* DISTINCT */
- testcase( i==108 ); /* IS */
- testcase( i==109 ); /* DROP */
- testcase( i==110 ); /* PRECEDING */
- testcase( i==111 ); /* FAIL */
- testcase( i==112 ); /* FILTER */
- testcase( i==113 ); /* REPLACE */
- testcase( i==114 ); /* FOLLOWING */
- testcase( i==115 ); /* FROM */
- testcase( i==116 ); /* FULL */
- testcase( i==117 ); /* IF */
- testcase( i==118 ); /* ISNULL */
- testcase( i==119 ); /* ORDER */
- testcase( i==120 ); /* RESTRICT */
- testcase( i==121 ); /* OVER */
- testcase( i==122 ); /* RIGHT */
- testcase( i==123 ); /* ROLLBACK */
- testcase( i==124 ); /* ROWS */
- testcase( i==125 ); /* ROW */
- testcase( i==126 ); /* UNBOUNDED */
- testcase( i==127 ); /* UNION */
- testcase( i==128 ); /* USING */
- testcase( i==129 ); /* VACUUM */
- testcase( i==130 ); /* VIEW */
- testcase( i==131 ); /* WINDOW */
- testcase( i==132 ); /* DO */
- testcase( i==133 ); /* INITIALLY */
- testcase( i==134 ); /* ALL */
- testcase( i==135 ); /* PRIMARY */
+ testcase( i==25 ); /* EXCLUDE */
+ testcase( i==26 ); /* DELETE */
+ testcase( i==27 ); /* TEMPORARY */
+ testcase( i==28 ); /* TEMP */
+ testcase( i==29 ); /* OR */
+ testcase( i==30 ); /* CONSTRAINT */
+ testcase( i==31 ); /* INTERSECT */
+ testcase( i==32 ); /* TIES */
+ testcase( i==33 ); /* SAVEPOINT */
+ testcase( i==34 ); /* INTO */
+ testcase( i==35 ); /* OFFSET */
+ testcase( i==36 ); /* OF */
+ testcase( i==37 ); /* SET */
+ testcase( i==38 ); /* TRANSACTION */
+ testcase( i==39 ); /* ACTION */
+ testcase( i==40 ); /* ON */
+ testcase( i==41 ); /* NATURAL */
+ testcase( i==42 ); /* ALTER */
+ testcase( i==43 ); /* RAISE */
+ testcase( i==44 ); /* EXCEPT */
+ testcase( i==45 ); /* TRIGGER */
+ testcase( i==46 ); /* REFERENCES */
+ testcase( i==47 ); /* UNIQUE */
+ testcase( i==48 ); /* QUERY */
+ testcase( i==49 ); /* WITHOUT */
+ testcase( i==50 ); /* WITH */
+ testcase( i==51 ); /* OUTER */
+ testcase( i==52 ); /* RELEASE */
+ testcase( i==53 ); /* EXCLUSIVE */
+ testcase( i==54 ); /* EXISTS */
+ testcase( i==55 ); /* ATTACH */
+ testcase( i==56 ); /* HAVING */
+ testcase( i==57 ); /* GLOB */
+ testcase( i==58 ); /* BEGIN */
+ testcase( i==59 ); /* INNER */
+ testcase( i==60 ); /* RANGE */
+ testcase( i==61 ); /* BETWEEN */
+ testcase( i==62 ); /* NOTHING */
+ testcase( i==63 ); /* GROUPS */
+ testcase( i==64 ); /* GROUP */
+ testcase( i==65 ); /* CASCADE */
+ testcase( i==66 ); /* ASC */
+ testcase( i==67 ); /* DETACH */
+ testcase( i==68 ); /* CASE */
+ testcase( i==69 ); /* COLLATE */
+ testcase( i==70 ); /* CREATE */
+ testcase( i==71 ); /* CURRENT_DATE */
+ testcase( i==72 ); /* IMMEDIATE */
+ testcase( i==73 ); /* JOIN */
+ testcase( i==74 ); /* INSERT */
+ testcase( i==75 ); /* LIKE */
+ testcase( i==76 ); /* MATCH */
+ testcase( i==77 ); /* PLAN */
+ testcase( i==78 ); /* ANALYZE */
+ testcase( i==79 ); /* PRAGMA */
+ testcase( i==80 ); /* ABORT */
+ testcase( i==81 ); /* UPDATE */
+ testcase( i==82 ); /* VALUES */
+ testcase( i==83 ); /* VIRTUAL */
+ testcase( i==84 ); /* LIMIT */
+ testcase( i==85 ); /* WHEN */
+ testcase( i==86 ); /* NOTNULL */
+ testcase( i==87 ); /* NOT */
+ testcase( i==88 ); /* NO */
+ testcase( i==89 ); /* NULL */
+ testcase( i==90 ); /* WHERE */
+ testcase( i==91 ); /* RECURSIVE */
+ testcase( i==92 ); /* AFTER */
+ testcase( i==93 ); /* RENAME */
+ testcase( i==94 ); /* AND */
+ testcase( i==95 ); /* DEFAULT */
+ testcase( i==96 ); /* AUTOINCREMENT */
+ testcase( i==97 ); /* TO */
+ testcase( i==98 ); /* IN */
+ testcase( i==99 ); /* CAST */
+ testcase( i==100 ); /* COLUMN */
+ testcase( i==101 ); /* COMMIT */
+ testcase( i==102 ); /* CONFLICT */
+ testcase( i==103 ); /* CROSS */
+ testcase( i==104 ); /* CURRENT_TIMESTAMP */
+ testcase( i==105 ); /* CURRENT_TIME */
+ testcase( i==106 ); /* CURRENT */
+ testcase( i==107 ); /* PARTITION */
+ testcase( i==108 ); /* DEFERRED */
+ testcase( i==109 ); /* DISTINCT */
+ testcase( i==110 ); /* IS */
+ testcase( i==111 ); /* DROP */
+ testcase( i==112 ); /* PRECEDING */
+ testcase( i==113 ); /* FAIL */
+ testcase( i==114 ); /* FILTER */
+ testcase( i==115 ); /* REPLACE */
+ testcase( i==116 ); /* FOLLOWING */
+ testcase( i==117 ); /* FROM */
+ testcase( i==118 ); /* FULL */
+ testcase( i==119 ); /* IF */
+ testcase( i==120 ); /* ISNULL */
+ testcase( i==121 ); /* ORDER */
+ testcase( i==122 ); /* RESTRICT */
+ testcase( i==123 ); /* OTHERS */
+ testcase( i==124 ); /* OVER */
+ testcase( i==125 ); /* RIGHT */
+ testcase( i==126 ); /* ROLLBACK */
+ testcase( i==127 ); /* ROWS */
+ testcase( i==128 ); /* ROW */
+ testcase( i==129 ); /* UNBOUNDED */
+ testcase( i==130 ); /* UNION */
+ testcase( i==131 ); /* USING */
+ testcase( i==132 ); /* VACUUM */
+ testcase( i==133 ); /* VIEW */
+ testcase( i==134 ); /* WINDOW */
+ testcase( i==135 ); /* DO */
+ testcase( i==136 ); /* BY */
+ testcase( i==137 ); /* INITIALLY */
+ testcase( i==138 ); /* ALL */
+ testcase( i==139 ); /* PRIMARY */
*pType = aKWCode[i];
break;
}
@@ -152581,7 +153490,7 @@ SQLITE_PRIVATE int sqlite3KeywordCode(const unsigned char *z, int n){
keywordCode((char*)z, n, &id);
return id;
}
-#define SQLITE_N_KEYWORD 136
+#define SQLITE_N_KEYWORD 140
SQLITE_API int sqlite3_keyword_name(int i,const char **pzName,int *pnName){
if( i<0 || i>=SQLITE_N_KEYWORD ) return SQLITE_ERROR;
*pzName = zKWText + aKWOffset[i];
@@ -153014,6 +153923,7 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr
#ifdef sqlite3Parser_ENGINEALWAYSONSTACK
yyParser sEngine; /* Space to hold the Lemon-generated Parser object */
#endif
+ VVA_ONLY( u8 startedWithOom = db->mallocFailed );
assert( zSql!=0 );
mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH];
@@ -153045,6 +153955,8 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr
assert( pParse->pNewTrigger==0 );
assert( pParse->nVar==0 );
assert( pParse->pVList==0 );
+ pParse->pParentParse = db->pParse;
+ db->pParse = pParse;
while( 1 ){
n = sqlite3GetToken((u8*)zSql, &tokenType);
mxSqlLen -= n;
@@ -153101,7 +154013,8 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr
sqlite3Parser(pEngine, tokenType, pParse->sLastToken);
lastTokenParsed = tokenType;
zSql += n;
- if( pParse->rc!=SQLITE_OK || db->mallocFailed ) break;
+ assert( db->mallocFailed==0 || pParse->rc!=SQLITE_OK || startedWithOom );
+ if( pParse->rc!=SQLITE_OK ) break;
}
assert( nErr==0 );
#ifdef YYTRACKMAXSTACKDEPTH
@@ -153169,6 +154082,8 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr
pParse->pZombieTab = p->pNextZombie;
sqlite3DeleteTable(db, p);
}
+ db->pParse = pParse->pParentParse;
+ pParse->pParentParse = 0;
assert( nErr==0 || pParse->rc!=SQLITE_OK );
return nErr;
}
@@ -154405,7 +155320,7 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
pStart = 0;
}else if( pBuf==0 ){
sqlite3BeginBenignMalloc();
- pStart = sqlite3Malloc( sz*cnt ); /* IMP: R-61949-35727 */
+ pStart = sqlite3Malloc( sz*(sqlite3_int64)cnt ); /* IMP: R-61949-35727 */
sqlite3EndBenignMalloc();
if( pStart ) cnt = sqlite3MallocSize(pStart)/sz;
}else{
@@ -154543,6 +155458,8 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){
{ SQLITE_DBCONFIG_TRIGGER_EQP, SQLITE_TriggerEQP },
{ SQLITE_DBCONFIG_RESET_DATABASE, SQLITE_ResetDatabase },
{ SQLITE_DBCONFIG_DEFENSIVE, SQLITE_Defensive },
+ { SQLITE_DBCONFIG_WRITABLE_SCHEMA, SQLITE_WriteSchema|
+ SQLITE_NoSchemaError },
};
unsigned int i;
rc = SQLITE_ERROR; /* IMP: R-42790-23372 */
@@ -161211,7 +162128,7 @@ static int fts3ScanInteriorNode(
zCsr += fts3GetVarint32(zCsr, &nSuffix);
assert( nPrefix>=0 && nSuffix>=0 );
- if( nPrefix>zCsr-zNode || nSuffix>zEnd-zCsr ){
+ if( nPrefix>zCsr-zNode || nSuffix>zEnd-zCsr || nSuffix==0 ){
rc = FTS_CORRUPT_VTAB;
goto finish_scan;
}
@@ -168324,7 +169241,7 @@ static void fts3TokenizerFunc(
nName = sqlite3_value_bytes(argv[0])+1;
if( argc==2 ){
- if( fts3TokenizerEnabled(context) ){
+ if( fts3TokenizerEnabled(context) || sqlite3_value_frombind(argv[1]) ){
void *pOld;
int n = sqlite3_value_bytes(argv[1]);
if( zName==0 || n!=sizeof(pPtr) ){
@@ -168351,7 +169268,9 @@ static void fts3TokenizerFunc(
return;
}
}
- sqlite3_result_blob(context, (void *)&pPtr, sizeof(pPtr), SQLITE_TRANSIENT);
+ if( fts3TokenizerEnabled(context) || sqlite3_value_frombind(argv[0]) ){
+ sqlite3_result_blob(context, (void *)&pPtr, sizeof(pPtr), SQLITE_TRANSIENT);
+ }
}
SQLITE_PRIVATE int sqlite3Fts3IsIdChar(char c){
@@ -168439,8 +169358,8 @@ SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(
int iArg = 0;
z = &z[n+1];
while( z<zEnd && (NULL!=(z = (char *)sqlite3Fts3NextToken(z, &n))) ){
- int nNew = sizeof(char *)*(iArg+1);
- char const **aNew = (const char **)sqlite3_realloc((void *)aArg, nNew);
+ sqlite3_int64 nNew = sizeof(char *)*(iArg+1);
+ char const **aNew = (const char **)sqlite3_realloc64((void *)aArg, nNew);
if( !aNew ){
sqlite3_free(zCopy);
sqlite3_free((void *)aArg);
@@ -169347,7 +170266,7 @@ static int fts3tokFilterMethod(
if( idxNum==1 ){
const char *zByte = (const char *)sqlite3_value_text(apVal[0]);
int nByte = sqlite3_value_bytes(apVal[0]);
- pCsr->zInput = sqlite3_malloc(nByte+1);
+ pCsr->zInput = sqlite3_malloc64(nByte+1);
if( pCsr->zInput==0 ){
rc = SQLITE_NOMEM;
}else{
@@ -170807,7 +171726,9 @@ static int fts3SegReaderNext(
/* If iCurrentBlock>=iLeafEndBlock, this is an EOF condition. All leaf
** blocks have already been traversed. */
- assert( pReader->iCurrentBlock<=pReader->iLeafEndBlock );
+#ifdef CORRUPT_DB
+ assert( pReader->iCurrentBlock<=pReader->iLeafEndBlock || CORRUPT_DB );
+#endif
if( pReader->iCurrentBlock>=pReader->iLeafEndBlock ){
return SQLITE_OK;
}
@@ -171209,8 +172130,9 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderPending(
}
if( nElem>0 ){
- int nByte = sizeof(Fts3SegReader) + (nElem+1)*sizeof(Fts3HashElem *);
- pReader = (Fts3SegReader *)sqlite3_malloc(nByte);
+ sqlite3_int64 nByte;
+ nByte = sizeof(Fts3SegReader) + (nElem+1)*sizeof(Fts3HashElem *);
+ pReader = (Fts3SegReader *)sqlite3_malloc64(nByte);
if( !pReader ){
rc = SQLITE_NOMEM;
}else{
@@ -172694,8 +173616,10 @@ static int fts3SegmentMerge(
if( rc!=SQLITE_OK ) goto finished;
assert( csr.nSegment>0 );
- assert( iNewLevel>=getAbsoluteLevel(p, iLangid, iIndex, 0) );
- assert( iNewLevel<getAbsoluteLevel(p, iLangid, iIndex,FTS3_SEGDIR_MAXLEVEL) );
+ assert_fts3_nc( iNewLevel>=getAbsoluteLevel(p, iLangid, iIndex, 0) );
+ assert_fts3_nc(
+ iNewLevel<getAbsoluteLevel(p, iLangid, iIndex,FTS3_SEGDIR_MAXLEVEL)
+ );
memset(&filter, 0, sizeof(Fts3SegFilter));
filter.flags = FTS3_SEGMENT_REQUIRE_POS;
@@ -172822,7 +173746,7 @@ static void fts3InsertDocsize(
int rc; /* Result code from subfunctions */
if( *pRC ) return;
- pBlob = sqlite3_malloc( 10*p->nColumn );
+ pBlob = sqlite3_malloc64( 10*(sqlite3_int64)p->nColumn );
if( pBlob==0 ){
*pRC = SQLITE_NOMEM;
return;
@@ -172872,7 +173796,7 @@ static void fts3UpdateDocTotals(
const int nStat = p->nColumn+2;
if( *pRC ) return;
- a = sqlite3_malloc( (sizeof(u32)+10)*nStat );
+ a = sqlite3_malloc64( (sizeof(u32)+10)*(sqlite3_int64)nStat );
if( a==0 ){
*pRC = SQLITE_NOMEM;
return;
@@ -172993,8 +173917,8 @@ static int fts3DoRebuild(Fts3Table *p){
}
if( rc==SQLITE_OK ){
- int nByte = sizeof(u32) * (p->nColumn+1)*3;
- aSz = (u32 *)sqlite3_malloc(nByte);
+ sqlite3_int64 nByte = sizeof(u32) * ((sqlite3_int64)p->nColumn+1)*3;
+ aSz = (u32 *)sqlite3_malloc64(nByte);
if( aSz==0 ){
rc = SQLITE_NOMEM;
}else{
@@ -173060,12 +173984,12 @@ static int fts3IncrmergeCsr(
){
int rc; /* Return Code */
sqlite3_stmt *pStmt = 0; /* Statement used to read %_segdir entry */
- int nByte; /* Bytes allocated at pCsr->apSegment[] */
+ sqlite3_int64 nByte; /* Bytes allocated at pCsr->apSegment[] */
/* Allocate space for the Fts3MultiSegReader.aCsr[] array */
memset(pCsr, 0, sizeof(*pCsr));
nByte = sizeof(Fts3SegReader *) * nSeg;
- pCsr->apSegment = (Fts3SegReader **)sqlite3_malloc(nByte);
+ pCsr->apSegment = (Fts3SegReader **)sqlite3_malloc64(nByte);
if( pCsr->apSegment==0 ){
rc = SQLITE_NOMEM;
@@ -175045,7 +175969,7 @@ SQLITE_PRIVATE int sqlite3Fts3UpdateMethod(
}
/* Allocate space to hold the change in document sizes */
- aSzDel = sqlite3_malloc( sizeof(aSzDel[0])*(p->nColumn+1)*2 );
+ aSzDel = sqlite3_malloc64(sizeof(aSzDel[0])*((sqlite3_int64)p->nColumn+1)*2);
if( aSzDel==0 ){
rc = SQLITE_NOMEM;
goto update_out;
@@ -175299,17 +176223,19 @@ struct StrBuffer {
/*
** Allocate a two-slot MatchinfoBuffer object.
*/
-static MatchinfoBuffer *fts3MIBufferNew(int nElem, const char *zMatchinfo){
+static MatchinfoBuffer *fts3MIBufferNew(size_t nElem, const char *zMatchinfo){
MatchinfoBuffer *pRet;
- int nByte = sizeof(u32) * (2*nElem + 1) + sizeof(MatchinfoBuffer);
- int nStr = (int)strlen(zMatchinfo);
+ sqlite3_int64 nByte = sizeof(u32) * (2*(sqlite3_int64)nElem + 1)
+ + sizeof(MatchinfoBuffer);
+ sqlite3_int64 nStr = strlen(zMatchinfo);
- pRet = sqlite3_malloc(nByte + nStr+1);
+ pRet = sqlite3_malloc64(nByte + nStr+1);
if( pRet ){
memset(pRet, 0, nByte);
pRet->aMatchinfo[0] = (u8*)(&pRet->aMatchinfo[1]) - (u8*)pRet;
- pRet->aMatchinfo[1+nElem] = pRet->aMatchinfo[0] + sizeof(u32)*(nElem+1);
- pRet->nElem = nElem;
+ pRet->aMatchinfo[1+nElem] = pRet->aMatchinfo[0]
+ + sizeof(u32)*((int)nElem+1);
+ pRet->nElem = (int)nElem;
pRet->zMatchinfo = ((char*)pRet) + nByte;
memcpy(pRet->zMatchinfo, zMatchinfo, nStr+1);
pRet->aRef[0] = 1;
@@ -175600,7 +176526,7 @@ static void fts3SnippetDetails(
char *pCsr = pPhrase->pTail;
int iCsr = pPhrase->iTail;
- while( iCsr<(iStart+pIter->nSnippet) ){
+ while( iCsr<(iStart+pIter->nSnippet) && iCsr>=iStart ){
int j;
u64 mPhrase = (u64)1 << i;
u64 mPos = (u64)1 << (iCsr - iStart);
@@ -176170,8 +177096,8 @@ static int fts3MatchinfoCheck(
return SQLITE_ERROR;
}
-static int fts3MatchinfoSize(MatchInfo *pInfo, char cArg){
- int nVal; /* Number of integers output by cArg */
+static size_t fts3MatchinfoSize(MatchInfo *pInfo, char cArg){
+ size_t nVal; /* Number of integers output by cArg */
switch( cArg ){
case FTS3_MATCHINFO_NDOC:
@@ -176455,7 +177381,7 @@ static int fts3MatchinfoValues(
case FTS3_MATCHINFO_LHITS_BM:
case FTS3_MATCHINFO_LHITS: {
- int nZero = fts3MatchinfoSize(pInfo, zArg[i]) * sizeof(u32);
+ size_t nZero = fts3MatchinfoSize(pInfo, zArg[i]) * sizeof(u32);
memset(pInfo->aMatchinfo, 0, nZero);
rc = fts3ExprLHitGather(pCsr->pExpr, pInfo);
break;
@@ -176524,7 +177450,7 @@ static void fts3GetMatchinfo(
** initialize those elements that are constant for every row.
*/
if( pCsr->pMIBuffer==0 ){
- int nMatchinfo = 0; /* Number of u32 elements in match-info */
+ size_t nMatchinfo = 0; /* Number of u32 elements in match-info */
int i; /* Used to iterate through zArg */
/* Determine the number of phrases in the query */
@@ -176714,7 +177640,7 @@ static int fts3ExprTermOffsetInit(Fts3Expr *pExpr, int iPhrase, void *ctx){
nTerm = pExpr->pPhrase->nToken;
if( pList ){
fts3GetDeltaPosition(&pList, &iPos);
- assert( iPos>=0 );
+ assert_fts3_nc( iPos>=0 );
}
for(iTerm=0; iTerm<nTerm; iTerm++){
@@ -176824,7 +177750,7 @@ SQLITE_PRIVATE void sqlite3Fts3Offsets(
/* All offsets for this column have been gathered. */
rc = SQLITE_DONE;
}else{
- assert( iCurrent<=iMinPos );
+ assert_fts3_nc( iCurrent<=iMinPos );
if( 0==(0xFE&*pTerm->pList) ){
pTerm->pList = 0;
}else{
@@ -183982,49 +184908,45 @@ rtreeInit_fail:
** <num-dimension>*2 coordinates.
*/
static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){
- char *zText = 0;
RtreeNode node;
Rtree tree;
int ii;
+ int nData;
+ int errCode;
+ sqlite3_str *pOut;
UNUSED_PARAMETER(nArg);
memset(&node, 0, sizeof(RtreeNode));
memset(&tree, 0, sizeof(Rtree));
tree.nDim = (u8)sqlite3_value_int(apArg[0]);
+ if( tree.nDim<1 || tree.nDim>5 ) return;
tree.nDim2 = tree.nDim*2;
tree.nBytesPerCell = 8 + 8 * tree.nDim;
node.zData = (u8 *)sqlite3_value_blob(apArg[1]);
+ nData = sqlite3_value_bytes(apArg[1]);
+ if( nData<4 ) return;
+ if( nData<NCELL(&node)*tree.nBytesPerCell ) return;
+ pOut = sqlite3_str_new(0);
for(ii=0; ii<NCELL(&node); ii++){
- char zCell[512];
- int nCell = 0;
RtreeCell cell;
int jj;
nodeGetCell(&tree, &node, ii, &cell);
- sqlite3_snprintf(512-nCell,&zCell[nCell],"%lld", cell.iRowid);
- nCell = (int)strlen(zCell);
+ if( ii>0 ) sqlite3_str_append(pOut, " ", 1);
+ sqlite3_str_appendf(pOut, "{%lld", cell.iRowid);
for(jj=0; jj<tree.nDim2; jj++){
#ifndef SQLITE_RTREE_INT_ONLY
- sqlite3_snprintf(512-nCell,&zCell[nCell], " %g",
- (double)cell.aCoord[jj].f);
+ sqlite3_str_appendf(pOut, " %g", (double)cell.aCoord[jj].f);
#else
- sqlite3_snprintf(512-nCell,&zCell[nCell], " %d",
- cell.aCoord[jj].i);
+ sqlite3_str_appendf(pOut, " %d", cell.aCoord[jj].i);
#endif
- nCell = (int)strlen(zCell);
- }
-
- if( zText ){
- char *zTextNew = sqlite3_mprintf("%s {%s}", zText, zCell);
- sqlite3_free(zText);
- zText = zTextNew;
- }else{
- zText = sqlite3_mprintf("{%s}", zCell);
}
+ sqlite3_str_append(pOut, "}", 1);
}
-
- sqlite3_result_text(ctx, zText, -1, sqlite3_free);
+ errCode = sqlite3_str_errcode(pOut);
+ sqlite3_result_text(ctx, sqlite3_str_finish(pOut), -1, sqlite3_free);
+ sqlite3_result_error_code(ctx, errCode);
}
/* This routine implements an SQL function that returns the "depth" parameter
@@ -184789,7 +185711,7 @@ static GeoPoly *geopolyParseJson(const unsigned char *z, int *pRc){
GeoPoly *pOut;
int x = 1;
s.nVertex--; /* Remove the redundant vertex at the end */
- pOut = sqlite3_malloc64( GEOPOLY_SZ(s.nVertex) );
+ pOut = sqlite3_malloc64( GEOPOLY_SZ((sqlite3_int64)s.nVertex) );
x = 1;
if( pOut==0 ) goto parse_json_err;
pOut->nVertex = s.nVertex;
@@ -185175,7 +186097,7 @@ static GeoPoly *geopolyBBox(
if( pRc ) *pRc = SQLITE_OK;
if( aCoord==0 ){
geopolyBboxFill:
- pOut = sqlite3_realloc(p, GEOPOLY_SZ(4));
+ pOut = sqlite3_realloc64(p, GEOPOLY_SZ(4));
if( pOut==0 ){
sqlite3_free(p);
if( context ) sqlite3_result_error_nomem(context);
@@ -185571,9 +186493,9 @@ static GeoSegment *geopolySortSegmentsByYAndC(GeoSegment *pList){
** Determine the overlap between two polygons
*/
static int geopolyOverlap(GeoPoly *p1, GeoPoly *p2){
- int nVertex = p1->nVertex + p2->nVertex + 2;
+ sqlite3_int64 nVertex = p1->nVertex + p2->nVertex + 2;
GeoOverlap *p;
- int nByte;
+ sqlite3_int64 nByte;
GeoEvent *pThisEvent;
double rX;
int rc = 0;
@@ -185585,7 +186507,7 @@ static int geopolyOverlap(GeoPoly *p1, GeoPoly *p2){
nByte = sizeof(GeoEvent)*nVertex*2
+ sizeof(GeoSegment)*nVertex
+ sizeof(GeoOverlap);
- p = sqlite3_malloc( nByte );
+ p = sqlite3_malloc64( nByte );
if( p==0 ) return -1;
p->aEvent = (GeoEvent*)&p[1];
p->aSegment = (GeoSegment*)&p->aEvent[nVertex*2];
@@ -185744,8 +186666,8 @@ static int geopolyInit(
){
int rc = SQLITE_OK;
Rtree *pRtree;
- int nDb; /* Length of string argv[1] */
- int nName; /* Length of string argv[2] */
+ sqlite3_int64 nDb; /* Length of string argv[1] */
+ sqlite3_int64 nName; /* Length of string argv[2] */
sqlite3_str *pSql;
char *zSql;
int ii;
@@ -185753,9 +186675,9 @@ static int geopolyInit(
sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
/* Allocate the sqlite3_vtab structure */
- nDb = (int)strlen(argv[1]);
- nName = (int)strlen(argv[2]);
- pRtree = (Rtree *)sqlite3_malloc(sizeof(Rtree)+nDb+nName+2);
+ nDb = strlen(argv[1]);
+ nName = strlen(argv[2]);
+ pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName+2);
if( !pRtree ){
return SQLITE_NOMEM;
}
@@ -188180,6 +189102,11 @@ struct RbuUpdateStmt {
** it points to an array of flags nTblCol elements in size. The flag is
** set for each column that is either a part of the PK or a part of an
** index. Or clear otherwise.
+**
+** If there are one or more partial indexes on the table, all fields of
+** this array set set to 1. This is because in that case, the module has
+** no way to tell which fields will be required to add and remove entries
+** from the partial indexes.
**
*/
struct RbuObjIter {
@@ -188624,6 +189551,7 @@ static void rbuFossilDeltaFunc(
}else{
nOut2 = rbuDeltaApply(aOrig, nOrig, aDelta, nDelta, aOut);
if( nOut2!=nOut ){
+ sqlite3_free(aOut);
sqlite3_result_error(context, "corrupt fossil delta", -1);
}else{
sqlite3_result_blob(context, aOut, nOut, sqlite3_free);
@@ -188974,7 +189902,7 @@ static int rbuMPrintfExec(sqlite3rbu *p, sqlite3 *db, const char *zFmt, ...){
** immediately without attempting the allocation or modifying the stored
** error code.
*/
-static void *rbuMalloc(sqlite3rbu *p, int nByte){
+static void *rbuMalloc(sqlite3rbu *p, sqlite3_int64 nByte){
void *pRet = 0;
if( p->rc==SQLITE_OK ){
assert( nByte>0 );
@@ -188995,7 +189923,7 @@ static void *rbuMalloc(sqlite3rbu *p, int nByte){
** error code in the RBU handle passed as the first argument.
*/
static void rbuAllocateIterArrays(sqlite3rbu *p, RbuObjIter *pIter, int nCol){
- int nByte = (2*sizeof(char*) + sizeof(int) + 3*sizeof(u8)) * nCol;
+ sqlite3_int64 nByte = (2*sizeof(char*) + sizeof(int) + 3*sizeof(u8)) * nCol;
char **azNew;
azNew = (char**)rbuMalloc(p, nByte);
@@ -189189,8 +190117,12 @@ static void rbuObjIterCacheIndexedCols(sqlite3rbu *p, RbuObjIter *pIter){
pIter->nIndex = 0;
while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pList) ){
const char *zIdx = (const char*)sqlite3_column_text(pList, 1);
+ int bPartial = sqlite3_column_int(pList, 4);
sqlite3_stmt *pXInfo = 0;
if( zIdx==0 ) break;
+ if( bPartial ){
+ memset(pIter->abIndexed, 0x01, sizeof(u8)*pIter->nTblCol);
+ }
p->rc = prepareFreeAndCollectError(p->dbMain, &pXInfo, &p->zErrmsg,
sqlite3_mprintf("PRAGMA main.index_xinfo = %Q", zIdx)
);
@@ -189635,7 +190567,7 @@ static char *rbuObjIterGetSetlist(
*/
static char *rbuObjIterGetBindlist(sqlite3rbu *p, int nBind){
char *zRet = 0;
- int nByte = nBind*2 + 1;
+ sqlite3_int64 nByte = 2*(sqlite3_int64)nBind + 1;
zRet = (char*)rbuMalloc(p, nByte);
if( zRet ){
@@ -189897,6 +190829,62 @@ static void rbuTmpInsertFunc(
}
}
+static char *rbuObjIterGetIndexWhere(sqlite3rbu *p, RbuObjIter *pIter){
+ sqlite3_stmt *pStmt = 0;
+ int rc = p->rc;
+ char *zRet = 0;
+
+ if( rc==SQLITE_OK ){
+ rc = prepareAndCollectError(p->dbMain, &pStmt, &p->zErrmsg,
+ "SELECT trim(sql) FROM sqlite_master WHERE type='index' AND name=?"
+ );
+ }
+ if( rc==SQLITE_OK ){
+ int rc2;
+ rc = sqlite3_bind_text(pStmt, 1, pIter->zIdx, -1, SQLITE_STATIC);
+ if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
+ const char *zSql = (const char*)sqlite3_column_text(pStmt, 0);
+ if( zSql ){
+ int nParen = 0; /* Number of open parenthesis */
+ int i;
+ for(i=0; zSql[i]; i++){
+ char c = zSql[i];
+ if( c=='(' ){
+ nParen++;
+ }
+ else if( c==')' ){
+ nParen--;
+ if( nParen==0 ){
+ i++;
+ break;
+ }
+ }else if( c=='"' || c=='\'' || c=='`' ){
+ for(i++; 1; i++){
+ if( zSql[i]==c ){
+ if( zSql[i+1]!=c ) break;
+ i++;
+ }
+ }
+ }else if( c=='[' ){
+ for(i++; 1; i++){
+ if( zSql[i]==']' ) break;
+ }
+ }
+ }
+ if( zSql[i] ){
+ zRet = rbuStrndup(&zSql[i], &rc);
+ }
+ }
+ }
+
+ rc2 = sqlite3_finalize(pStmt);
+ if( rc==SQLITE_OK ) rc = rc2;
+ }
+
+ p->rc = rc;
+ return zRet;
+}
+
/*
** Ensure that the SQLite statement handles required to update the
** target database object currently indicated by the iterator passed
@@ -189926,6 +190914,7 @@ static int rbuObjIterPrepareAll(
char *zImposterPK = 0; /* Primary key declaration for imposter */
char *zWhere = 0; /* WHERE clause on PK columns */
char *zBind = 0;
+ char *zPart = 0;
int nBind = 0;
assert( pIter->eType!=RBU_PK_VTAB );
@@ -189933,6 +190922,7 @@ static int rbuObjIterPrepareAll(
p, pIter, &zImposterCols, &zImposterPK, &zWhere, &nBind
);
zBind = rbuObjIterGetBindlist(p, nBind);
+ zPart = rbuObjIterGetIndexWhere(p, pIter);
/* Create the imposter table used to write to this index. */
sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 0, 1);
@@ -189965,28 +190955,30 @@ static int rbuObjIterPrepareAll(
char *zSql;
if( rbuIsVacuum(p) ){
zSql = sqlite3_mprintf(
- "SELECT %s, 0 AS rbu_control FROM '%q' ORDER BY %s%s",
+ "SELECT %s, 0 AS rbu_control FROM '%q' %s ORDER BY %s%s",
zCollist,
pIter->zDataTbl,
- zCollist, zLimit
+ zPart, zCollist, zLimit
);
}else
if( pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE ){
zSql = sqlite3_mprintf(
- "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' ORDER BY %s%s",
+ "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' %s ORDER BY %s%s",
zCollist, p->zStateDb, pIter->zDataTbl,
- zCollist, zLimit
+ zPart, zCollist, zLimit
);
}else{
zSql = sqlite3_mprintf(
- "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' "
+ "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' %s "
"UNION ALL "
"SELECT %s, rbu_control FROM '%q' "
- "WHERE typeof(rbu_control)='integer' AND rbu_control!=1 "
+ "%s %s typeof(rbu_control)='integer' AND rbu_control!=1 "
"ORDER BY %s%s",
- zCollist, p->zStateDb, pIter->zDataTbl,
+ zCollist, p->zStateDb, pIter->zDataTbl, zPart,
zCollist, pIter->zDataTbl,
+ zPart,
+ (zPart ? "AND" : "WHERE"),
zCollist, zLimit
);
}
@@ -189997,6 +190989,7 @@ static int rbuObjIterPrepareAll(
sqlite3_free(zImposterPK);
sqlite3_free(zWhere);
sqlite3_free(zBind);
+ sqlite3_free(zPart);
}else{
int bRbuRowid = (pIter->eType==RBU_PK_VTAB)
||(pIter->eType==RBU_PK_NONE)
@@ -192430,7 +193423,7 @@ static int rbuVfsShmMap(
assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) );
if( eStage==RBU_STAGE_OAL || eStage==RBU_STAGE_MOVE ){
if( iRegion<=p->nShm ){
- int nByte = (iRegion+1) * sizeof(char*);
+ sqlite3_int64 nByte = (iRegion+1) * sizeof(char*);
char **apNew = (char**)sqlite3_realloc64(p->apShm, nByte);
if( apNew==0 ){
rc = SQLITE_NOMEM;
@@ -194941,7 +195934,7 @@ static int sessionGrowHash(int bPatchset, SessionTable *pTab){
if( pTab->nChange==0 || pTab->nEntry>=(pTab->nChange/2) ){
int i;
SessionChange **apNew;
- int nNew = (pTab->nChange ? pTab->nChange : 128) * 2;
+ sqlite3_int64 nNew = 2*(sqlite3_int64)(pTab->nChange ? pTab->nChange : 128);
apNew = (SessionChange **)sqlite3_malloc64(sizeof(SessionChange *) * nNew);
if( apNew==0 ){
@@ -195868,7 +196861,7 @@ SQLITE_API int sqlite3session_attach(
** If successful, return zero. Otherwise, if an OOM condition is encountered,
** set *pRc to SQLITE_NOMEM and return non-zero.
*/
-static int sessionBufferGrow(SessionBuffer *p, int nByte, int *pRc){
+static int sessionBufferGrow(SessionBuffer *p, size_t nByte, int *pRc){
if( *pRc==SQLITE_OK && p->nAlloc-p->nBuf<nByte ){
u8 *aNew;
i64 nNew = p->nAlloc ? p->nAlloc : 128;
@@ -196986,7 +197979,7 @@ static int sessionChangesetReadTblhdr(sqlite3_changeset_iter *p){
}
if( rc==SQLITE_OK ){
- int iPK = sizeof(sqlite3_value*)*p->nCol*2;
+ size_t iPK = sizeof(sqlite3_value*)*p->nCol*2;
memset(p->tblhdr.aBuf, 0, iPK);
memcpy(&p->tblhdr.aBuf[iPK], &p->in.aData[p->in.iNext], nCopy);
p->in.iNext += nCopy;
@@ -197901,7 +198894,7 @@ static int sessionSeekToRow(
}
/*
-** This function is called from within sqlite3changset_apply_v2() when
+** This function is called from within sqlite3changeset_apply_v2() when
** a conflict is encountered and resolved using conflict resolution
** mode eType (either SQLITE_CHANGESET_OMIT or SQLITE_CHANGESET_REPLACE)..
** It adds a conflict resolution record to the buffer in
@@ -198290,7 +199283,7 @@ static int sessionRetryConstraints(
rc = sessionChangesetStart(&pIter2, 0, 0, cons.nBuf, cons.aBuf, 0);
if( rc==SQLITE_OK ){
- int nByte = 2*pApply->nCol*sizeof(sqlite3_value*);
+ size_t nByte = 2*pApply->nCol*sizeof(sqlite3_value*);
int rc2;
pIter2->bPatchset = bPatchset;
pIter2->zTab = (char*)zTab;
@@ -199683,7 +200676,7 @@ struct Fts5PhraseIter {
** Save the pointer passed as the second argument as the extension functions
** "auxiliary data". The pointer may then be retrieved by the current or any
** future invocation of the same fts5 extension function made as part of
-** of the same MATCH query using the xGetAuxdata() API.
+** the same MATCH query using the xGetAuxdata() API.
**
** Each extension function is allocated a single auxiliary data slot for
** each FTS query (MATCH expression). If the extension function is invoked
@@ -199698,7 +200691,7 @@ struct Fts5PhraseIter {
** The xDelete callback, if one is specified, is also invoked on the
** auxiliary data pointer after the FTS5 query has finished.
**
-** If an error (e.g. an OOM condition) occurs within this function, an
+** If an error (e.g. an OOM condition) occurs within this function,
** the auxiliary data is set to NULL and an error code returned. If the
** xDelete parameter was not NULL, it is invoked on the auxiliary data
** pointer before returning.
@@ -200680,8 +201673,9 @@ static void sqlite3Fts5HashClear(Fts5Hash*);
static int sqlite3Fts5HashQuery(
Fts5Hash*, /* Hash table to query */
+ int nPre,
const char *pTerm, int nTerm, /* Query term */
- const u8 **ppDoclist, /* OUT: Pointer to doclist for pTerm */
+ void **ppObj, /* OUT: Pointer to doclist for pTerm */
int *pnDoclist /* OUT: Size of doclist in bytes */
);
@@ -202751,7 +203745,7 @@ static int fts5SnippetScore(
sqlite3_int64 iAdj = iFirst - (nToken - (iLast-iFirst)) / 2;
if( (iAdj+nToken)>nDocsize ) iAdj = nDocsize - nToken;
if( iAdj<0 ) iAdj = 0;
- *piPos = iAdj;
+ *piPos = (int)iAdj;
}
return rc;
@@ -202979,7 +203973,7 @@ static int fts5Bm25GetData(
if( p==0 ){
rc = SQLITE_NOMEM;
}else{
- memset(p, 0, nByte);
+ memset(p, 0, (size_t)nByte);
p->nPhrase = nPhrase;
p->aIDF = (double*)&p[1];
p->aFreq = &p->aIDF[nPhrase];
@@ -203142,7 +204136,7 @@ static int sqlite3Fts5BufferSize(int *pRc, Fts5Buffer *pBuf, u32 nByte){
*pRc = SQLITE_NOMEM;
return 1;
}else{
- pBuf->nSpace = nNew;
+ pBuf->nSpace = (int)nNew;
pBuf->p = pNew;
}
}
@@ -203366,7 +204360,7 @@ static void *sqlite3Fts5MallocZero(int *pRc, sqlite3_int64 nByte){
if( pRet==0 ){
if( nByte>0 ) *pRc = SQLITE_NOMEM;
}else{
- memset(pRet, 0, nByte);
+ memset(pRet, 0, (size_t)nByte);
}
}
return pRet;
@@ -203835,7 +204829,7 @@ static int fts5ConfigParseSpecial(
rc = SQLITE_ERROR;
}else{
rc = sqlite3Fts5GetTokenizer(pGlobal,
- (const char**)azArg, nArg, &pConfig->pTok, &pConfig->pTokApi,
+ (const char**)azArg, (int)nArg, &pConfig->pTok, &pConfig->pTokApi,
pzErr
);
}
@@ -203945,7 +204939,7 @@ static const char *fts5ConfigGobbleWord(
if( zOut==0 ){
*pRc = SQLITE_NOMEM;
}else{
- memcpy(zOut, zIn, nIn+1);
+ memcpy(zOut, zIn, (size_t)(nIn+1));
if( fts5_isopenquote(zOut[0]) ){
int ii = fts5Dequote(zOut);
zRet = &zIn[ii];
@@ -205959,7 +206953,7 @@ static Fts5ExprNearset *sqlite3Fts5ParseNearset(
if( pRet==0 ){
pParse->rc = SQLITE_NOMEM;
}else{
- memset(pRet, 0, nByte);
+ memset(pRet, 0, (size_t)nByte);
}
}else if( (pNear->nPhrase % SZALLOC)==0 ){
int nNew = pNear->nPhrase + SZALLOC;
@@ -206035,7 +207029,7 @@ static int fts5ParseTokenize(
if( pSyn==0 ){
rc = SQLITE_NOMEM;
}else{
- memset(pSyn, 0, nByte);
+ memset(pSyn, 0, (size_t)nByte);
pSyn->zTerm = ((char*)pSyn) + sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer);
memcpy(pSyn->zTerm, pToken, nToken);
pSyn->pSynonym = pPhrase->aTerm[pPhrase->nTerm-1].pSynonym;
@@ -206195,7 +207189,7 @@ static int sqlite3Fts5ExprClonePhrase(
nByte = sizeof(Fts5Colset) + (pColsetOrig->nCol-1) * sizeof(int);
pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte);
if( pColset ){
- memcpy(pColset, pColsetOrig, nByte);
+ memcpy(pColset, pColsetOrig, (size_t)nByte);
}
pNew->pRoot->pNear->pColset = pColset;
}
@@ -206412,7 +207406,7 @@ static Fts5Colset *fts5CloneColset(int *pRc, Fts5Colset *pOrig){
sqlite3_int64 nByte = sizeof(Fts5Colset) + (pOrig->nCol-1) * sizeof(int);
pRet = (Fts5Colset*)sqlite3Fts5MallocZero(pRc, nByte);
if( pRet ){
- memcpy(pRet, pOrig, nByte);
+ memcpy(pRet, pOrig, (size_t)nByte);
}
}else{
pRet = 0;
@@ -207429,7 +208423,7 @@ static int sqlite3Fts5HashNew(Fts5Config *pConfig, Fts5Hash **ppNew, int *pnByte
*ppNew = 0;
rc = SQLITE_NOMEM;
}else{
- memset(pNew->aSlot, 0, nByte);
+ memset(pNew->aSlot, 0, (size_t)nByte);
}
}
return rc;
@@ -207513,19 +208507,25 @@ static int fts5HashResize(Fts5Hash *pHash){
return SQLITE_OK;
}
-static void fts5HashAddPoslistSize(Fts5Hash *pHash, Fts5HashEntry *p){
+static int fts5HashAddPoslistSize(
+ Fts5Hash *pHash,
+ Fts5HashEntry *p,
+ Fts5HashEntry *p2
+){
+ int nRet = 0;
if( p->iSzPoslist ){
- u8 *pPtr = (u8*)p;
+ u8 *pPtr = p2 ? (u8*)p2 : (u8*)p;
+ int nData = p->nData;
if( pHash->eDetail==FTS5_DETAIL_NONE ){
- assert( p->nData==p->iSzPoslist );
+ assert( nData==p->iSzPoslist );
if( p->bDel ){
- pPtr[p->nData++] = 0x00;
+ pPtr[nData++] = 0x00;
if( p->bContent ){
- pPtr[p->nData++] = 0x00;
+ pPtr[nData++] = 0x00;
}
}
}else{
- int nSz = (p->nData - p->iSzPoslist - 1); /* Size in bytes */
+ int nSz = (nData - p->iSzPoslist - 1); /* Size in bytes */
int nPos = nSz*2 + p->bDel; /* Value of nPos field */
assert( p->bDel==0 || p->bDel==1 );
@@ -207535,14 +208535,19 @@ static void fts5HashAddPoslistSize(Fts5Hash *pHash, Fts5HashEntry *p){
int nByte = sqlite3Fts5GetVarintLen((u32)nPos);
memmove(&pPtr[p->iSzPoslist + nByte], &pPtr[p->iSzPoslist + 1], nSz);
sqlite3Fts5PutVarint(&pPtr[p->iSzPoslist], nPos);
- p->nData += (nByte-1);
+ nData += (nByte-1);
}
}
- p->iSzPoslist = 0;
- p->bDel = 0;
- p->bContent = 0;
+ nRet = nData - p->nData;
+ if( p2==0 ){
+ p->iSzPoslist = 0;
+ p->bDel = 0;
+ p->bContent = 0;
+ p->nData = nData;
+ }
}
+ return nRet;
}
/*
@@ -207599,7 +208604,7 @@ static int sqlite3Fts5HashWrite(
p = (Fts5HashEntry*)sqlite3_malloc64(nByte);
if( !p ) return SQLITE_NOMEM;
memset(p, 0, sizeof(Fts5HashEntry));
- p->nAlloc = nByte;
+ p->nAlloc = (int)nByte;
zKey = fts5EntryKey(p);
zKey[0] = bByte;
memcpy(&zKey[1], pToken, nToken);
@@ -207654,7 +208659,7 @@ static int sqlite3Fts5HashWrite(
/* If this is a new rowid, append the 4-byte size field for the previous
** entry, and the new rowid for this entry. */
if( iRowid!=p->iRowid ){
- fts5HashAddPoslistSize(pHash, p);
+ fts5HashAddPoslistSize(pHash, p, 0);
p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iRowid - p->iRowid);
p->iRowid = iRowid;
bNew = 1;
@@ -207771,7 +208776,9 @@ static int fts5HashEntrySort(
for(iSlot=0; iSlot<pHash->nSlot; iSlot++){
Fts5HashEntry *pIter;
for(pIter=pHash->aSlot[iSlot]; pIter; pIter=pIter->pHashNext){
- if( pTerm==0 || 0==memcmp(fts5EntryKey(pIter), pTerm, nTerm) ){
+ if( pTerm==0
+ || (pIter->nKey+1>=nTerm && 0==memcmp(fts5EntryKey(pIter), pTerm, nTerm))
+ ){
Fts5HashEntry *pEntry = pIter;
pEntry->pScanNext = 0;
for(i=0; ap[i]; i++){
@@ -207799,8 +208806,9 @@ static int fts5HashEntrySort(
*/
static int sqlite3Fts5HashQuery(
Fts5Hash *pHash, /* Hash table to query */
+ int nPre,
const char *pTerm, int nTerm, /* Query term */
- const u8 **ppDoclist, /* OUT: Pointer to doclist for pTerm */
+ void **ppOut, /* OUT: Pointer to new object */
int *pnDoclist /* OUT: Size of doclist in bytes */
){
unsigned int iHash = fts5HashKey(pHash->nSlot, (const u8*)pTerm, nTerm);
@@ -207814,11 +208822,20 @@ static int sqlite3Fts5HashQuery(
}
if( p ){
- fts5HashAddPoslistSize(pHash, p);
- *ppDoclist = (const u8*)&zKey[nTerm+1];
- *pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm + 1);
+ int nHashPre = sizeof(Fts5HashEntry) + nTerm + 1;
+ int nList = p->nData - nHashPre;
+ u8 *pRet = (u8*)(*ppOut = sqlite3_malloc64(nPre + nList + 10));
+ if( pRet ){
+ Fts5HashEntry *pFaux = (Fts5HashEntry*)&pRet[nPre-nHashPre];
+ memcpy(&pRet[nPre], &((u8*)p)[nHashPre], nList);
+ nList += fts5HashAddPoslistSize(pHash, p, pFaux);
+ *pnDoclist = nList;
+ }else{
+ *pnDoclist = 0;
+ return SQLITE_NOMEM;
+ }
}else{
- *ppDoclist = 0;
+ *ppOut = 0;
*pnDoclist = 0;
}
@@ -207851,7 +208868,7 @@ static void sqlite3Fts5HashScanEntry(
if( (p = pHash->pScan) ){
char *zKey = fts5EntryKey(p);
int nTerm = (int)strlen(zKey);
- fts5HashAddPoslistSize(pHash, p);
+ fts5HashAddPoslistSize(pHash, p, 0);
*pzTerm = zKey;
*ppDoclist = (const u8*)&zKey[nTerm+1];
*pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm + 1);
@@ -210321,31 +211338,40 @@ static void fts5SegIterHashInit(
int flags, /* Mask of FTS5INDEX_XXX flags */
Fts5SegIter *pIter /* Object to populate */
){
- const u8 *pList = 0;
int nList = 0;
const u8 *z = 0;
int n = 0;
+ Fts5Data *pLeaf = 0;
assert( p->pHash );
assert( p->rc==SQLITE_OK );
if( pTerm==0 || (flags & FTS5INDEX_QUERY_SCAN) ){
+ const u8 *pList = 0;
+
p->rc = sqlite3Fts5HashScanInit(p->pHash, (const char*)pTerm, nTerm);
sqlite3Fts5HashScanEntry(p->pHash, (const char**)&z, &pList, &nList);
n = (z ? (int)strlen((const char*)z) : 0);
+ if( pList ){
+ pLeaf = fts5IdxMalloc(p, sizeof(Fts5Data));
+ if( pLeaf ){
+ pLeaf->p = (u8*)pList;
+ }
+ }
}else{
- pIter->flags |= FTS5_SEGITER_ONETERM;
- sqlite3Fts5HashQuery(p->pHash, (const char*)pTerm, nTerm, &pList, &nList);
+ p->rc = sqlite3Fts5HashQuery(p->pHash, sizeof(Fts5Data),
+ (const char*)pTerm, nTerm, (void**)&pLeaf, &nList
+ );
+ if( pLeaf ){
+ pLeaf->p = (u8*)&pLeaf[1];
+ }
z = pTerm;
n = nTerm;
+ pIter->flags |= FTS5_SEGITER_ONETERM;
}
- if( pList ){
- Fts5Data *pLeaf;
+ if( pLeaf ){
sqlite3Fts5BufferSet(&p->rc, &pIter->term, n, z);
- pLeaf = fts5IdxMalloc(p, sizeof(Fts5Data));
- if( pLeaf==0 ) return;
- pLeaf->p = (u8*)pList;
pLeaf->nn = pLeaf->szLeaf = nList;
pIter->pLeaf = pLeaf;
pIter->iLeafOffset = fts5GetVarint(pLeaf->p, (u64*)&pIter->iRowid);
@@ -210498,8 +211524,8 @@ static int fts5MultiIterDoCompare(Fts5Iter *pIter, int iOut){
}else{
int res = fts5BufferCompare(&p1->term, &p2->term);
if( res==0 ){
- assert( i2>i1 );
- assert( i2!=0 );
+ assert_nc( i2>i1 );
+ assert_nc( i2!=0 );
pRes->bTermEq = 1;
if( p1->iRowid==p2->iRowid ){
p1->bDel = p2->bDel;
@@ -211546,7 +212572,7 @@ static int fts5WriteDlidxGrow(
if( aDlidx==0 ){
p->rc = SQLITE_NOMEM;
}else{
- int nByte = sizeof(Fts5DlidxWriter) * (nLvl - pWriter->nDlidx);
+ size_t nByte = sizeof(Fts5DlidxWriter) * (nLvl - pWriter->nDlidx);
memset(&aDlidx[pWriter->nDlidx], 0, nByte);
pWriter->aDlidx = aDlidx;
pWriter->nDlidx = nLvl;
@@ -212033,13 +213059,14 @@ static void fts5TrimSegments(Fts5Index *p, Fts5Iter *pIter){
/* Set up the new page-index array */
fts5BufferAppendVarint(&p->rc, &buf, 4);
if( pSeg->iLeafPgno==pSeg->iTermLeafPgno
- && pSeg->iEndofDoclist<pData->szLeaf
- ){
+ && pSeg->iEndofDoclist<pData->szLeaf
+ && pSeg->iPgidxOff<=pData->nn
+ ){
int nDiff = pData->szLeaf - pSeg->iEndofDoclist;
fts5BufferAppendVarint(&p->rc, &buf, buf.n - 1 - nDiff - 4);
fts5BufferAppendBlob(&p->rc, &buf,
pData->nn - pSeg->iPgidxOff, &pData->p[pSeg->iPgidxOff]
- );
+ );
}
pSeg->pSeg->pgnoFirst = pSeg->iTermLeafPgno;
@@ -215061,7 +216088,7 @@ static int fts5OpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){
pCsr = (Fts5Cursor*)sqlite3_malloc64(nByte);
if( pCsr ){
Fts5Global *pGlobal = pTab->pGlobal;
- memset(pCsr, 0, nByte);
+ memset(pCsr, 0, (size_t)nByte);
pCsr->aColumnSize = (int*)&pCsr[1];
pCsr->pNext = pGlobal->pCsr;
pGlobal->pCsr = pCsr;
@@ -215342,7 +216369,7 @@ static int fts5CursorFirstSorted(
nByte = sizeof(Fts5Sorter) + sizeof(int) * (nPhrase-1);
pSorter = (Fts5Sorter*)sqlite3_malloc64(nByte);
if( pSorter==0 ) return SQLITE_NOMEM;
- memset(pSorter, 0, nByte);
+ memset(pSorter, 0, (size_t)nByte);
pSorter->nIdx = nPhrase;
/* TODO: It would be better to have some system for reusing statement
@@ -216896,14 +217923,14 @@ static int fts5CreateAux(
int rc = sqlite3_overload_function(pGlobal->db, zName, -1);
if( rc==SQLITE_OK ){
Fts5Auxiliary *pAux;
- int nName; /* Size of zName in bytes, including \0 */
- int nByte; /* Bytes of space to allocate */
+ sqlite3_int64 nName; /* Size of zName in bytes, including \0 */
+ sqlite3_int64 nByte; /* Bytes of space to allocate */
- nName = (int)strlen(zName) + 1;
+ nName = strlen(zName) + 1;
nByte = sizeof(Fts5Auxiliary) + nName;
- pAux = (Fts5Auxiliary*)sqlite3_malloc(nByte);
+ pAux = (Fts5Auxiliary*)sqlite3_malloc64(nByte);
if( pAux ){
- memset(pAux, 0, nByte);
+ memset(pAux, 0, (size_t)nByte);
pAux->zFunc = (char*)&pAux[1];
memcpy(pAux->zFunc, zName, nName);
pAux->pGlobal = pGlobal;
@@ -216933,15 +217960,15 @@ static int fts5CreateTokenizer(
){
Fts5Global *pGlobal = (Fts5Global*)pApi;
Fts5TokenizerModule *pNew;
- int nName; /* Size of zName and its \0 terminator */
- int nByte; /* Bytes of space to allocate */
+ sqlite3_int64 nName; /* Size of zName and its \0 terminator */
+ sqlite3_int64 nByte; /* Bytes of space to allocate */
int rc = SQLITE_OK;
- nName = (int)strlen(zName) + 1;
+ nName = strlen(zName) + 1;
nByte = sizeof(Fts5TokenizerModule) + nName;
- pNew = (Fts5TokenizerModule*)sqlite3_malloc(nByte);
+ pNew = (Fts5TokenizerModule*)sqlite3_malloc64(nByte);
if( pNew ){
- memset(pNew, 0, nByte);
+ memset(pNew, 0, (size_t)nByte);
pNew->zName = (char*)&pNew[1];
memcpy(pNew->zName, zName, nName);
pNew->pUserData = pUserData;
@@ -217076,7 +218103,7 @@ static void fts5SourceIdFunc(
){
assert( nArg==0 );
UNUSED_PARAM2(nArg, apUnused);
- sqlite3_result_text(pCtx, "fts5: 2019-02-08 13:17:39 0eca3dd3d38b31c92b49ca2d311128b74584714d9e7de895b1a6286ef959a1dd", -1, SQLITE_TRANSIENT);
+ sqlite3_result_text(pCtx, "fts5: 2019-04-16 19:49:53 884b4b7e502b4e991677b53971277adfaf0a04a284f8e483e2553d0f83156b50", -1, SQLITE_TRANSIENT);
}
/*
@@ -217499,7 +218526,7 @@ static int sqlite3Fts5StorageOpen(
*pp = p = (Fts5Storage*)sqlite3_malloc64(nByte);
if( !p ) return SQLITE_NOMEM;
- memset(p, 0, nByte);
+ memset(p, 0, (size_t)nByte);
p->aTotalSize = (i64*)&p[1];
p->pConfig = pConfig;
p->pIndex = pIndex;
@@ -218721,7 +219748,7 @@ static int fts5UnicodeCreate(
p->eRemoveDiacritic = FTS5_REMOVE_DIACRITICS_SIMPLE;
p->nFold = 64;
- p->aFold = sqlite3_malloc(p->nFold * sizeof(char));
+ p->aFold = sqlite3_malloc64(p->nFold * sizeof(char));
if( p->aFold==0 ){
rc = SQLITE_NOMEM;
}
@@ -220409,7 +221436,7 @@ static void sqlite3Fts5UnicodeAscii(u8 *aArray, u8 *aAscii){
int bToken = aArray[ aFts5UnicodeData[iTbl] & 0x1F ];
int n = (aFts5UnicodeData[iTbl] >> 5) + i;
for(; i<128 && i<n; i++){
- aAscii[i] = bToken;
+ aAscii[i] = (u8)bToken;
}
iTbl++;
}
@@ -221840,9 +222867,9 @@ SQLITE_API int sqlite3_stmt_init(
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */
/************** End of stmt.c ************************************************/
-#if __LINE__!=221843
+#if __LINE__!=222870
#undef SQLITE_SOURCE_ID
-#define SQLITE_SOURCE_ID "2019-02-08 13:17:39 0eca3dd3d38b31c92b49ca2d311128b74584714d9e7de895b1a6286ef959alt2"
+#define SQLITE_SOURCE_ID "2019-04-16 19:49:53 884b4b7e502b4e991677b53971277adfaf0a04a284f8e483e2553d0f8315alt2"
#endif
/* Return the source-id for this library */
SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
diff --git a/src/3rdparty/sqlite/sqlite3.h b/src/3rdparty/sqlite/sqlite3.h
index 686aa8b739..fadfe1e152 100644
--- a/src/3rdparty/sqlite/sqlite3.h
+++ b/src/3rdparty/sqlite/sqlite3.h
@@ -123,9 +123,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.27.1"
-#define SQLITE_VERSION_NUMBER 3027001
-#define SQLITE_SOURCE_ID "2019-02-08 13:17:39 0eca3dd3d38b31c92b49ca2d311128b74584714d9e7de895b1a6286ef959a1dd"
+#define SQLITE_VERSION "3.28.0"
+#define SQLITE_VERSION_NUMBER 3028000
+#define SQLITE_SOURCE_ID "2019-04-16 19:49:53 884b4b7e502b4e991677b53971277adfaf0a04a284f8e483e2553d0f83156b50"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -189,6 +189,9 @@ SQLITE_API int sqlite3_libversion_number(void);
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
SQLITE_API int sqlite3_compileoption_used(const char *zOptName);
SQLITE_API const char *sqlite3_compileoption_get(int N);
+#else
+# define sqlite3_compileoption_used(X) 0
+# define sqlite3_compileoption_get(X) ((void*)0)
#endif
/*
@@ -2086,8 +2089,8 @@ struct sqlite3_mem_methods {
**
** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
-** <dd> ^This option is used to enable or disable the two-argument
-** version of the [fts3_tokenizer()] function which is part of the
+** <dd> ^This option is used to enable or disable the
+** [fts3_tokenizer()] function which is part of the
** [FTS3] full-text search engine extension.
** There should be two additional arguments.
** The first argument is an integer which is 0 to disable fts3_tokenizer() or
@@ -2199,6 +2202,17 @@ struct sqlite3_mem_methods {
** <li> Direct writes to [shadow tables].
** </ul>
** </dd>
+**
+** [[SQLITE_DBCONFIG_WRITABLE_SCHEMA]] <dt>SQLITE_DBCONFIG_WRITABLE_SCHEMA</dt>
+** <dd>The SQLITE_DBCONFIG_WRITABLE_SCHEMA option activates or deactivates the
+** "writable_schema" flag. This has the same effect and is logically equivalent
+** to setting [PRAGMA writable_schema=ON] or [PRAGMA writable_schema=OFF].
+** The first argument to this setting is an integer which is 0 to disable
+** the writable_schema, positive to enable writable_schema, or negative to
+** leave the setting unchanged. The second parameter is a pointer to an
+** integer into which is written 0 or 1 to indicate whether the writable_schema
+** is enabled or disabled following this call.
+** </dd>
** </dl>
*/
#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
@@ -2212,7 +2226,8 @@ struct sqlite3_mem_methods {
#define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */
#define SQLITE_DBCONFIG_RESET_DATABASE 1009 /* int int* */
#define SQLITE_DBCONFIG_DEFENSIVE 1010 /* int int* */
-#define SQLITE_DBCONFIG_MAX 1010 /* Largest DBCONFIG */
+#define SQLITE_DBCONFIG_WRITABLE_SCHEMA 1011 /* int int* */
+#define SQLITE_DBCONFIG_MAX 1011 /* Largest DBCONFIG */
/*
** CAPI3REF: Enable Or Disable Extended Result Codes
@@ -2369,7 +2384,7 @@ SQLITE_API int sqlite3_changes(sqlite3*);
** not. ^Changes to a view that are intercepted by INSTEAD OF triggers
** are not counted.
**
-** This the [sqlite3_total_changes(D)] interface only reports the number
+** The [sqlite3_total_changes(D)] interface only reports the number
** of rows that changed due to SQL statement run against database
** connection D. Any changes by other database connections are ignored.
** To detect changes against a database file from other database
@@ -3895,6 +3910,18 @@ SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt);
SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt);
/*
+** CAPI3REF: Query The EXPLAIN Setting For A Prepared Statement
+** METHOD: sqlite3_stmt
+**
+** ^The sqlite3_stmt_isexplain(S) interface returns 1 if the
+** prepared statement S is an EXPLAIN statement, or 2 if the
+** statement S is an EXPLAIN QUERY PLAN.
+** ^The sqlite3_stmt_isexplain(S) interface returns 0 if S is
+** an ordinary statement or a NULL pointer.
+*/
+SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt);
+
+/*
** CAPI3REF: Determine If A Prepared Statement Has Been Reset
** METHOD: sqlite3_stmt
**
@@ -4033,7 +4060,9 @@ typedef struct sqlite3_context sqlite3_context;
** ^The fifth argument to the BLOB and string binding interfaces
** is a destructor used to dispose of the BLOB or
** string after SQLite has finished with it. ^The destructor is called
-** to dispose of the BLOB or string even if the call to bind API fails.
+** to dispose of the BLOB or string even if the call to the bind API fails,
+** except the destructor is not called if the third parameter is a NULL
+** pointer or the fourth parameter is negative.
** ^If the fifth argument is
** the special value [SQLITE_STATIC], then SQLite assumes that the
** information is in static, unmanaged space and does not need to be freed.
@@ -4950,6 +4979,8 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6
** <tr><td><b>sqlite3_value_nochange&nbsp;&nbsp;</b>
** <td>&rarr;&nbsp;&nbsp;<td>True if the column is unchanged in an UPDATE
** against a virtual table.
+** <tr><td><b>sqlite3_value_frombind&nbsp;&nbsp;</b>
+** <td>&rarr;&nbsp;&nbsp;<td>True if value originated from a [bound parameter]
** </table></blockquote>
**
** <b>Details:</b>
@@ -5011,6 +5042,11 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6
** than within an [xUpdate] method call for an UPDATE statement, then
** the return value is arbitrary and meaningless.
**
+** ^The sqlite3_value_frombind(X) interface returns non-zero if the
+** value X originated from one of the [sqlite3_bind_int|sqlite3_bind()]
+** interfaces. ^If X comes from an SQL literal value, or a table column,
+** and expression, then sqlite3_value_frombind(X) returns zero.
+**
** Please pay particular attention to the fact that the pointer returned
** from [sqlite3_value_blob()], [sqlite3_value_text()], or
** [sqlite3_value_text16()] can be invalidated by a subsequent call to
@@ -5056,6 +5092,7 @@ SQLITE_API int sqlite3_value_bytes16(sqlite3_value*);
SQLITE_API int sqlite3_value_type(sqlite3_value*);
SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);
SQLITE_API int sqlite3_value_nochange(sqlite3_value*);
+SQLITE_API int sqlite3_value_frombind(sqlite3_value*);
/*
** CAPI3REF: Finding The Subtype Of SQL Values
@@ -5791,7 +5828,7 @@ SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*);
** associated with database N of connection D. ^The main database file
** has the name "main". If there is no attached database N on the database
** connection D, or if database N is a temporary or in-memory database, then
-** a NULL pointer is returned.
+** this function will return either a NULL pointer or an empty string.
**
** ^The filename returned by this function is the output of the
** xFullPathname method of the [VFS]. ^In other words, the filename
@@ -10892,7 +10929,7 @@ SQLITE_API int sqlite3rebaser_configure(
** in size. This function allocates and populates a buffer with a copy
** of the changeset rebased rebased according to the configuration of the
** rebaser object passed as the first argument. If successful, (*ppOut)
-** is set to point to the new buffer containing the rebased changset and
+** is set to point to the new buffer containing the rebased changeset and
** (*pnOut) to its size in bytes and SQLITE_OK returned. It is the
** responsibility of the caller to eventually free the new buffer using
** sqlite3_free(). Otherwise, if an error occurs, (*ppOut) and (*pnOut)
@@ -11301,7 +11338,7 @@ struct Fts5PhraseIter {
** Save the pointer passed as the second argument as the extension functions
** "auxiliary data". The pointer may then be retrieved by the current or any
** future invocation of the same fts5 extension function made as part of
-** of the same MATCH query using the xGetAuxdata() API.
+** the same MATCH query using the xGetAuxdata() API.
**
** Each extension function is allocated a single auxiliary data slot for
** each FTS query (MATCH expression). If the extension function is invoked
@@ -11316,7 +11353,7 @@ struct Fts5PhraseIter {
** The xDelete callback, if one is specified, is also invoked on the
** auxiliary data pointer after the FTS5 query has finished.
**
-** If an error (e.g. an OOM condition) occurs within this function, an
+** If an error (e.g. an OOM condition) occurs within this function,
** the auxiliary data is set to NULL and an error code returned. If the
** xDelete parameter was not NULL, it is invoked on the auxiliary data
** pointer before returning.
diff --git a/src/android/templates/build.gradle b/src/android/templates/build.gradle
index 989d0792cf..5754c82708 100644
--- a/src/android/templates/build.gradle
+++ b/src/android/templates/build.gradle
@@ -44,7 +44,7 @@ android {
java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java']
aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl']
res.srcDirs = [qt5AndroidDir + '/res', 'res']
- resources.srcDirs = ['src']
+ resources.srcDirs = ['resources']
renderscript.srcDirs = ['src']
assets.srcDirs = ['assets']
jniLibs.srcDirs = ['libs']
diff --git a/src/concurrent/qtconcurrentiteratekernel.h b/src/concurrent/qtconcurrentiteratekernel.h
index 89fd3d2592..3095c9ff52 100644
--- a/src/concurrent/qtconcurrentiteratekernel.h
+++ b/src/concurrent/qtconcurrentiteratekernel.h
@@ -206,9 +206,9 @@ public:
bool shouldStartThread() override
{
if (forIteration)
- return (currentIndex.load() < iterationCount) && !this->shouldThrottleThread();
+ return (currentIndex.loadRelaxed() < iterationCount) && !this->shouldThrottleThread();
else // whileIteration
- return (iteratorThreads.load() == 0);
+ return (iteratorThreads.loadRelaxed() == 0);
}
ThreadFunctionResult threadFunction() override
@@ -230,7 +230,7 @@ public:
const int currentBlockSize = blockSizeManager.blockSize();
- if (currentIndex.load() >= iterationCount)
+ if (currentIndex.loadRelaxed() >= iterationCount)
break;
// Atomically reserve a block of iterationCount for this thread.
@@ -261,7 +261,7 @@ public:
// Report progress if progress reporting enabled.
if (progressReportingEnabled) {
completed.fetchAndAddAcquire(finalBlockSize);
- this->setProgressValue(this->completed.load());
+ this->setProgressValue(this->completed.loadRelaxed());
}
if (this->shouldThrottleThread())
diff --git a/src/concurrent/qtconcurrentrun.cpp b/src/concurrent/qtconcurrentrun.cpp
index bbd1f5ec62..d9867e1f1a 100644
--- a/src/concurrent/qtconcurrentrun.cpp
+++ b/src/concurrent/qtconcurrentrun.cpp
@@ -138,10 +138,10 @@
T is the same type as the return value of \a function. Non-void return
values can be accessed via the QFuture::result() function.
- Note that the QFuture returned by QtConcurrent::run() does not support
- canceling, pausing, or progress reporting. The QFuture returned can only
- be used to query for the running/finished status and the return value of
- the function.
+ \note The QFuture returned can only be used to query for the
+ running/finished status and the return value of the function. In particular,
+ canceling or pausing can be issued only if the computations behind the future
+ has not been started.
\sa {Concurrent Run}
*/
@@ -157,10 +157,10 @@
T is the same type as the return value of \a function. Non-void return
values can be accessed via the QFuture::result() function.
- Note that the QFuture returned by QtConcurrent::run() does not support
- canceling, pausing, or progress reporting. The QFuture returned can only
- be used to query for the running/finished status and the return value of
- the function.
+ \note The QFuture returned can only be used to query for the
+ running/finished status and the return value of the function. In particular,
+ canceling or pausing can be issued only if the computations behind the future
+ has not been started.
\sa {Concurrent Run}
*/
diff --git a/src/concurrent/qtconcurrentthreadengine.cpp b/src/concurrent/qtconcurrentthreadengine.cpp
index 968720cbbe..7f91a2ba68 100644
--- a/src/concurrent/qtconcurrentthreadengine.cpp
+++ b/src/concurrent/qtconcurrentthreadengine.cpp
@@ -91,7 +91,7 @@ ThreadEngineBarrier::ThreadEngineBarrier()
void ThreadEngineBarrier::acquire()
{
forever {
- int localCount = count.load();
+ int localCount = count.loadRelaxed();
if (localCount < 0) {
if (count.testAndSetOrdered(localCount, localCount -1))
return;
@@ -105,7 +105,7 @@ void ThreadEngineBarrier::acquire()
int ThreadEngineBarrier::release()
{
forever {
- int localCount = count.load();
+ int localCount = count.loadRelaxed();
if (localCount == -1) {
if (count.testAndSetOrdered(-1, 0)) {
semaphore.release();
@@ -125,7 +125,7 @@ int ThreadEngineBarrier::release()
void ThreadEngineBarrier::wait()
{
forever {
- int localCount = count.load();
+ int localCount = count.loadRelaxed();
if (localCount == 0)
return;
@@ -139,7 +139,7 @@ void ThreadEngineBarrier::wait()
int ThreadEngineBarrier::currentCount()
{
- return count.load();
+ return count.loadRelaxed();
}
// releases a thread, unless this is the last thread.
@@ -147,7 +147,7 @@ int ThreadEngineBarrier::currentCount()
bool ThreadEngineBarrier::releaseUnlessLast()
{
forever {
- int localCount = count.load();
+ int localCount = count.loadRelaxed();
if (qAbs(localCount) == 1) {
return false;
} else if (localCount < 0) {
diff --git a/src/corelib/Qt5CoreMacros.cmake b/src/corelib/Qt5CoreMacros.cmake
index 78b99f5bfe..0f006fe1e3 100644
--- a/src/corelib/Qt5CoreMacros.cmake
+++ b/src/corelib/Qt5CoreMacros.cmake
@@ -296,6 +296,9 @@ endfunction()
# qt5_add_big_resources(outfiles inputfile ... )
function(QT5_ADD_BIG_RESOURCES outfiles )
+ if (CMAKE_VERSION VERSION_LESS 3.9)
+ message(FATAL_ERROR, "qt5_add_big_resources requires CMake 3.9 or newer")
+ endif()
set(options)
set(oneValueArgs)
@@ -326,6 +329,8 @@ function(QT5_ADD_BIG_RESOURCES outfiles )
set_target_properties(rcc_object_${outfilename} PROPERTIES AUTOMOC OFF)
set_target_properties(rcc_object_${outfilename} PROPERTIES AUTOUIC OFF)
add_dependencies(rcc_object_${outfilename} big_resources_${outfilename})
+ # The modification of TARGET_OBJECTS needs the following change in cmake
+ # https://gitlab.kitware.com/cmake/cmake/commit/93c89bc75ceee599ba7c08b8fe1ac5104942054f
add_custom_command(OUTPUT ${outfile}
COMMAND ${Qt5Core_RCC_EXECUTABLE}
ARGS ${rcc_options} --name ${outfilename} --pass 2 --temp $<TARGET_OBJECTS:rcc_object_${outfilename}> --output ${outfile} ${infile}
diff --git a/src/corelib/animation/qabstractanimation.cpp b/src/corelib/animation/qabstractanimation.cpp
index 0be37b7dca..46b01449d4 100644
--- a/src/corelib/animation/qabstractanimation.cpp
+++ b/src/corelib/animation/qabstractanimation.cpp
@@ -1063,10 +1063,12 @@ QAbstractAnimation::~QAbstractAnimation()
if (d->state != Stopped) {
QAbstractAnimation::State oldState = d->state;
d->state = Stopped;
- emit stateChanged(oldState, d->state);
+ emit stateChanged(d->state, oldState);
if (oldState == QAbstractAnimation::Running)
QAnimationTimer::unregisterAnimation(this);
}
+ if (d->group)
+ d->group->removeAnimation(this);
}
/*!
diff --git a/src/corelib/animation/qanimationgroup.cpp b/src/corelib/animation/qanimationgroup.cpp
index f47d99eb68..ed40817222 100644
--- a/src/corelib/animation/qanimationgroup.cpp
+++ b/src/corelib/animation/qanimationgroup.cpp
@@ -113,6 +113,11 @@ QAnimationGroup::QAnimationGroup(QAnimationGroupPrivate &dd, QObject *parent)
*/
QAnimationGroup::~QAnimationGroup()
{
+ Q_D(QAnimationGroup);
+ // We need to clear the animations now while we are still a valid QAnimationGroup.
+ // If we wait until ~QObject() the QAbstractAnimation's pointer back to us would
+ // point to a QObject, not a valid QAnimationGroup.
+ d->clear(true);
}
/*!
@@ -256,7 +261,7 @@ QAbstractAnimation *QAnimationGroup::takeAnimation(int index)
void QAnimationGroup::clear()
{
Q_D(QAnimationGroup);
- qDeleteAll(d->animations);
+ d->clear(false);
}
/*!
@@ -284,6 +289,24 @@ bool QAnimationGroup::event(QEvent *event)
return QAbstractAnimation::event(event);
}
+void QAnimationGroupPrivate::clear(bool onDestruction)
+{
+ const QList<QAbstractAnimation *> animationsCopy = animations; // taking a copy
+ animations.clear();
+ // Clearing backwards so the indices doesn't change while we remove animations.
+ for (int i = animationsCopy.count() - 1; i >= 0; --i) {
+ QAbstractAnimation *animation = animationsCopy.at(i);
+ animation->setParent(nullptr);
+ QAbstractAnimationPrivate::get(animation)->group = nullptr;
+ // If we are in ~QAnimationGroup() it is not safe to called the virtual
+ // animationRemoved method, which can still be a method in a
+ // QAnimationGroupPrivate derived class that assumes q_ptr is still
+ // a valid derived class of QAnimationGroup.
+ if (!onDestruction)
+ animationRemoved(i, animation);
+ delete animation;
+ }
+}
void QAnimationGroupPrivate::animationRemoved(int index, QAbstractAnimation *)
{
diff --git a/src/corelib/animation/qanimationgroup_p.h b/src/corelib/animation/qanimationgroup_p.h
index 215734b656..4d1d690e69 100644
--- a/src/corelib/animation/qanimationgroup_p.h
+++ b/src/corelib/animation/qanimationgroup_p.h
@@ -73,6 +73,8 @@ public:
virtual void animationInsertedAt(int) { }
virtual void animationRemoved(int, QAbstractAnimation *);
+ void clear(bool onDestruction);
+
void disconnectUncontrolledAnimation(QAbstractAnimation *anim)
{
//0 for the signal here because we might be called from the animation destructor
diff --git a/src/corelib/animation/qsequentialanimationgroup.cpp b/src/corelib/animation/qsequentialanimationgroup.cpp
index 150e74d7d6..66e346a2fe 100644
--- a/src/corelib/animation/qsequentialanimationgroup.cpp
+++ b/src/corelib/animation/qsequentialanimationgroup.cpp
@@ -532,7 +532,8 @@ void QSequentialAnimationGroupPrivate::animationRemoved(int index, QAbstractAnim
Q_Q(QSequentialAnimationGroup);
QAnimationGroupPrivate::animationRemoved(index, anim);
- Q_ASSERT(currentAnimation); // currentAnimation should always be set
+ if (!currentAnimation)
+ return;
if (actualDuration.size() > index)
actualDuration.removeAt(index);
diff --git a/src/corelib/animation/qvariantanimation.cpp b/src/corelib/animation/qvariantanimation.cpp
index ac81f89ed4..01a699c5dc 100644
--- a/src/corelib/animation/qvariantanimation.cpp
+++ b/src/corelib/animation/qvariantanimation.cpp
@@ -283,11 +283,11 @@ void QVariantAnimationPrivate::setCurrentValueForProgress(const qreal progress)
qSwap(currentValue, ret);
q->updateCurrentValue(currentValue);
static QBasicAtomicInt changedSignalIndex = Q_BASIC_ATOMIC_INITIALIZER(0);
- if (!changedSignalIndex.load()) {
+ if (!changedSignalIndex.loadRelaxed()) {
//we keep the mask so that we emit valueChanged only when needed (for performance reasons)
changedSignalIndex.testAndSetRelaxed(0, signalIndex("valueChanged(QVariant)"));
}
- if (isSignalConnected(changedSignalIndex.load()) && currentValue != ret) {
+ if (isSignalConnected(changedSignalIndex.loadRelaxed()) && currentValue != ret) {
//the value has changed
emit q->valueChanged(currentValue);
}
diff --git a/src/corelib/codecs/qlatincodec.cpp b/src/corelib/codecs/qlatincodec.cpp
index 1b53d26ef4..463c5a56ae 100644
--- a/src/corelib/codecs/qlatincodec.cpp
+++ b/src/corelib/codecs/qlatincodec.cpp
@@ -48,7 +48,7 @@ QLatin1Codec::~QLatin1Codec()
QString QLatin1Codec::convertToUnicode(const char *chars, int len, ConverterState *) const
{
- if (chars == 0)
+ if (chars == nullptr)
return QString();
return QString::fromLatin1(chars, len);
@@ -104,7 +104,7 @@ QLatin15Codec::~QLatin15Codec()
QString QLatin15Codec::convertToUnicode(const char* chars, int len, ConverterState *) const
{
- if (chars == 0)
+ if (chars == nullptr)
return QString();
QString str = QString::fromLatin1(chars, len);
diff --git a/src/corelib/codecs/qsimplecodec.cpp b/src/corelib/codecs/qsimplecodec.cpp
index 9ab545d783..16a9b8a7c3 100644
--- a/src/corelib/codecs/qsimplecodec.cpp
+++ b/src/corelib/codecs/qsimplecodec.cpp
@@ -610,7 +610,7 @@ QSimpleTextCodec::QSimpleTextCodec(int i) : forwardIndex(i), reverseMap(0)
QSimpleTextCodec::~QSimpleTextCodec()
{
- delete reverseMap.load();
+ delete reverseMap.loadAcquire();
}
static QByteArray *buildReverseMap(int forwardIndex)
@@ -662,12 +662,12 @@ QByteArray QSimpleTextCodec::convertFromUnicode(const QChar *in, int length, Con
const char replacement = (state && state->flags & ConvertInvalidToNull) ? 0 : '?';
int invalid = 0;
- QByteArray *rmap = reverseMap.load();
+ QByteArray *rmap = reverseMap.loadAcquire();
if (!rmap){
rmap = buildReverseMap(this->forwardIndex);
if (!reverseMap.testAndSetRelease(0, rmap)) {
delete rmap;
- rmap = reverseMap.load();
+ rmap = reverseMap.loadAcquire();
}
}
diff --git a/src/corelib/codecs/qtextcodec.cpp b/src/corelib/codecs/qtextcodec.cpp
index a86db142f9..85cfcdbf48 100644
--- a/src/corelib/codecs/qtextcodec.cpp
+++ b/src/corelib/codecs/qtextcodec.cpp
@@ -159,7 +159,7 @@ static QTextCodec *setupLocaleMapper()
{
QCoreGlobalData *globalData = QCoreGlobalData::instance();
- QTextCodec *locale = 0;
+ QTextCodec *locale = nullptr;
{
QMutexLocker locker(textCodecsMutex());
@@ -208,7 +208,7 @@ static QTextCodec *setupLocaleMapper()
// First part is getting that locale name. First try setlocale() which
// definitely knows it, but since we cannot fully trust it, get ready
// to fall back to environment variables.
- const QByteArray ctype = setlocale(LC_CTYPE, 0);
+ const QByteArray ctype = setlocale(LC_CTYPE, nullptr);
// Get the first nonempty value from $LC_ALL, $LC_CTYPE, and $LANG
// environment variables.
@@ -532,13 +532,13 @@ QTextCodec::~QTextCodec()
QTextCodec *QTextCodec::codecForName(const QByteArray &name)
{
if (name.isEmpty())
- return 0;
+ return nullptr;
QMutexLocker locker(textCodecsMutex());
QCoreGlobalData *globalData = QCoreGlobalData::instance();
if (!globalData)
- return 0;
+ return nullptr;
setup();
#if !QT_CONFIG(icu)
@@ -567,7 +567,7 @@ QTextCodec *QTextCodec::codecForName(const QByteArray &name)
}
}
- return 0;
+ return nullptr;
#else
return QIcuCodec::codecForNameUnlocked(name);
#endif
@@ -585,7 +585,7 @@ QTextCodec* QTextCodec::codecForMib(int mib)
QCoreGlobalData *globalData = QCoreGlobalData::instance();
if (!globalData)
- return 0;
+ return nullptr;
if (globalData->allCodecs.isEmpty())
setup();
@@ -611,7 +611,7 @@ QTextCodec* QTextCodec::codecForMib(int mib)
#if QT_CONFIG(icu)
return QIcuCodec::codecForMibUnlocked(mib);
#else
- return 0;
+ return nullptr;
#endif
}
@@ -704,7 +704,7 @@ QTextCodec* QTextCodec::codecForLocale()
{
QCoreGlobalData *globalData = QCoreGlobalData::instance();
if (!globalData)
- return 0;
+ return nullptr;
QTextCodec *codec = globalData->codecForLocale.loadAcquire();
if (!codec) {
@@ -830,7 +830,7 @@ QTextEncoder* QTextCodec::makeEncoder(QTextCodec::ConversionFlags flags) const
*/
QByteArray QTextCodec::fromUnicode(const QString& str) const
{
- return convertFromUnicode(str.constData(), str.length(), 0);
+ return convertFromUnicode(str.constData(), str.length(), nullptr);
}
#endif
@@ -863,7 +863,7 @@ QByteArray QTextCodec::fromUnicode(QStringView str) const
*/
QString QTextCodec::toUnicode(const QByteArray& a) const
{
- return convertToUnicode(a.constData(), a.length(), 0);
+ return convertToUnicode(a.constData(), a.length(), nullptr);
}
/*!
@@ -915,7 +915,7 @@ bool QTextCodec::canEncode(QStringView s) const
QString QTextCodec::toUnicode(const char *chars) const
{
int len = qstrlen(chars);
- return convertToUnicode(chars, len, 0);
+ return convertToUnicode(chars, len, nullptr);
}
@@ -1110,7 +1110,7 @@ QString QTextDecoder::toUnicode(const QByteArray &ba)
QTextCodec *QTextCodec::codecForHtml(const QByteArray &ba, QTextCodec *defaultCodec)
{
// determine charset
- QTextCodec *c = QTextCodec::codecForUtfText(ba, 0);
+ QTextCodec *c = QTextCodec::codecForUtfText(ba, nullptr);
if (!c) {
static Q_RELAXED_CONSTEXPR auto matcher = qMakeStaticByteArrayMatcher("meta ");
QByteArray header = ba.left(1024).toLower();
diff --git a/src/corelib/doc/snippets/code/src_corelib_tools_qregularexpression.cpp b/src/corelib/doc/snippets/code/src_corelib_tools_qregularexpression.cpp
index afdd9c3d25..99cd4ea7a2 100644
--- a/src/corelib/doc/snippets/code/src_corelib_tools_qregularexpression.cpp
+++ b/src/corelib/doc/snippets/code/src_corelib_tools_qregularexpression.cpp
@@ -286,15 +286,11 @@ if (!invalidRe.isValid()) {
{
//! [24]
-QRegularExpression re("^this pattern must match exactly$");
-//! [24]
-}
-
-{
-//! [25]
QString p("a .*|pattern");
-QRegularExpression re("\\A(?:" + p + ")\\z"); // re matches exactly the pattern string p
-//! [25]
+
+// re matches exactly the pattern string p
+QRegularExpression re(QRegularExpression::anchoredPattern(p));
+//! [24]
}
{
diff --git a/src/corelib/doc/src/cmake-macros.qdoc b/src/corelib/doc/src/cmake-macros.qdoc
index 6140e8be44..7fb133020f 100644
--- a/src/corelib/doc/src/cmake-macros.qdoc
+++ b/src/corelib/doc/src/cmake-macros.qdoc
@@ -131,6 +131,8 @@ files (\c .o, \c .obj) files instead of C++ source code. This allows to
embed bigger resources, where compiling to C++ sources and then to
binaries would be too time consuming or memory intensive.
+Note that this macro is only available if using \c{CMake} 3.9 or later.
+
\section1 Arguments
You can set additional \c{OPTIONS} that should be added to the \c{rcc} calls.
diff --git a/src/corelib/doc/src/dontdocument.qdoc b/src/corelib/doc/src/dontdocument.qdoc
new file mode 100644
index 0000000000..19ca7db299
--- /dev/null
+++ b/src/corelib/doc/src/dontdocument.qdoc
@@ -0,0 +1,41 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \dontdocument (QMacAutoReleasePool QIncompatibleFlag QGenericAtomicOps QAtomicTraits
+ QAtomicOps QBasicAtomicInteger QBasicAtomicPointer QBasicMutex QInternal
+ QArgument QReturnArgument QArrayData QTypedArrayData QStaticByteArrayData
+ QByteRef QStaticStringData QListSpecialMethods QListData QScopedPointerDeleter
+ QScopedPointerArrayDeleter QScopedPointerPodDeleter QScopedPointerObjectDeleteLater
+ QMetaTypeId2 QObjectData QObjectUserData QMapNodeBase QMapNode QMapDataBase
+ QMapData QHashData QHashNode QArrayDataPointer QTextStreamManipulator
+ QContiguousCacheData QContiguousCacheTypedData QNoDebug QUrlTwoFlags
+ QCborValueRef qfloat16 QDeferredDeleteEvent QSpecialInteger QLittleEndianStorageType
+ QBigEndianStorageType QFactoryInterface QFutureWatcherBase QJsonValuePtr
+ QJsonValueRefPtr QLinkedListNode QAbstractConcatenable QStringBuilderCommon
+ QTextCodec::ConverterState QThreadStorageData)
+*/
diff --git a/src/corelib/global/global.pri b/src/corelib/global/global.pri
index 10af46b41a..1da69aba9b 100644
--- a/src/corelib/global/global.pri
+++ b/src/corelib/global/global.pri
@@ -40,6 +40,10 @@ SOURCES += \
global/qrandom.cpp \
global/qhooks.cpp
+# To get listed in IDEs
+false: SOURCES += \
+ global/qfloat16tables.cpp
+
# Only add global/qfloat16_f16c.c if qfloat16.cpp can't #include it.
# Any compiler: if it is already generating F16C code, let qfloat16.cpp do it
# Clang: ICE if not generating F16C code, so use qfloat16_f16c.c
@@ -124,14 +128,3 @@ gcc:ltcg {
} else {
SOURCES += $$VERSIONTAGGING_SOURCES
}
-
-QMAKE_QFLOAT16_TABLES_GENERATE = global/qfloat16.h
-
-qtPrepareTool(QMAKE_QFLOAT16_TABLES, qfloat16-tables)
-
-qfloat16_tables.commands = $$QMAKE_QFLOAT16_TABLES ${QMAKE_FILE_OUT}
-qfloat16_tables.output = global/qfloat16tables.cpp
-qfloat16_tables.depends = $$QMAKE_QFLOAT16_TABLES_EXE
-qfloat16_tables.input = QMAKE_QFLOAT16_TABLES_GENERATE
-qfloat16_tables.variable_out = SOURCES
-QMAKE_EXTRA_COMPILERS += qfloat16_tables
diff --git a/src/corelib/global/qfloat16.cpp b/src/corelib/global/qfloat16.cpp
index 68763c0606..ff2997b73a 100644
--- a/src/corelib/global/qfloat16.cpp
+++ b/src/corelib/global/qfloat16.cpp
@@ -239,6 +239,7 @@ Q_CORE_EXPORT void qFloatFromFloat16(float *out, const qfloat16 *in, qsizetype l
QT_END_NAMESPACE
+#include "qfloat16tables.cpp"
#ifdef QFLOAT16_INCLUDE_FAST
# include "qfloat16_f16c.c"
#endif
diff --git a/src/corelib/global/qfloat16tables.cpp b/src/corelib/global/qfloat16tables.cpp
new file mode 100644
index 0000000000..3d764937d7
--- /dev/null
+++ b/src/corelib/global/qfloat16tables.cpp
@@ -0,0 +1,3266 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 by Southwest Research Institute (R)
+** Copyright (C) 2019 Intel Corporation.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/* This file was generated by gen_qfloat16_tables.cpp */
+
+#include <QtCore/qfloat16.h>
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(__F16C__) && !defined(__ARM_FP16_FORMAT_IEEE)
+
+const quint32 qfloat16::mantissatable[2048] = {
+0,
+0x33800000U,
+0x34000000U,
+0x34400000U,
+0x34800000U,
+0x34A00000U,
+0x34C00000U,
+0x34E00000U,
+0x35000000U,
+0x35100000U,
+0x35200000U,
+0x35300000U,
+0x35400000U,
+0x35500000U,
+0x35600000U,
+0x35700000U,
+0x35800000U,
+0x35880000U,
+0x35900000U,
+0x35980000U,
+0x35A00000U,
+0x35A80000U,
+0x35B00000U,
+0x35B80000U,
+0x35C00000U,
+0x35C80000U,
+0x35D00000U,
+0x35D80000U,
+0x35E00000U,
+0x35E80000U,
+0x35F00000U,
+0x35F80000U,
+0x36000000U,
+0x36040000U,
+0x36080000U,
+0x360C0000U,
+0x36100000U,
+0x36140000U,
+0x36180000U,
+0x361C0000U,
+0x36200000U,
+0x36240000U,
+0x36280000U,
+0x362C0000U,
+0x36300000U,
+0x36340000U,
+0x36380000U,
+0x363C0000U,
+0x36400000U,
+0x36440000U,
+0x36480000U,
+0x364C0000U,
+0x36500000U,
+0x36540000U,
+0x36580000U,
+0x365C0000U,
+0x36600000U,
+0x36640000U,
+0x36680000U,
+0x366C0000U,
+0x36700000U,
+0x36740000U,
+0x36780000U,
+0x367C0000U,
+0x36800000U,
+0x36820000U,
+0x36840000U,
+0x36860000U,
+0x36880000U,
+0x368A0000U,
+0x368C0000U,
+0x368E0000U,
+0x36900000U,
+0x36920000U,
+0x36940000U,
+0x36960000U,
+0x36980000U,
+0x369A0000U,
+0x369C0000U,
+0x369E0000U,
+0x36A00000U,
+0x36A20000U,
+0x36A40000U,
+0x36A60000U,
+0x36A80000U,
+0x36AA0000U,
+0x36AC0000U,
+0x36AE0000U,
+0x36B00000U,
+0x36B20000U,
+0x36B40000U,
+0x36B60000U,
+0x36B80000U,
+0x36BA0000U,
+0x36BC0000U,
+0x36BE0000U,
+0x36C00000U,
+0x36C20000U,
+0x36C40000U,
+0x36C60000U,
+0x36C80000U,
+0x36CA0000U,
+0x36CC0000U,
+0x36CE0000U,
+0x36D00000U,
+0x36D20000U,
+0x36D40000U,
+0x36D60000U,
+0x36D80000U,
+0x36DA0000U,
+0x36DC0000U,
+0x36DE0000U,
+0x36E00000U,
+0x36E20000U,
+0x36E40000U,
+0x36E60000U,
+0x36E80000U,
+0x36EA0000U,
+0x36EC0000U,
+0x36EE0000U,
+0x36F00000U,
+0x36F20000U,
+0x36F40000U,
+0x36F60000U,
+0x36F80000U,
+0x36FA0000U,
+0x36FC0000U,
+0x36FE0000U,
+0x37000000U,
+0x37010000U,
+0x37020000U,
+0x37030000U,
+0x37040000U,
+0x37050000U,
+0x37060000U,
+0x37070000U,
+0x37080000U,
+0x37090000U,
+0x370A0000U,
+0x370B0000U,
+0x370C0000U,
+0x370D0000U,
+0x370E0000U,
+0x370F0000U,
+0x37100000U,
+0x37110000U,
+0x37120000U,
+0x37130000U,
+0x37140000U,
+0x37150000U,
+0x37160000U,
+0x37170000U,
+0x37180000U,
+0x37190000U,
+0x371A0000U,
+0x371B0000U,
+0x371C0000U,
+0x371D0000U,
+0x371E0000U,
+0x371F0000U,
+0x37200000U,
+0x37210000U,
+0x37220000U,
+0x37230000U,
+0x37240000U,
+0x37250000U,
+0x37260000U,
+0x37270000U,
+0x37280000U,
+0x37290000U,
+0x372A0000U,
+0x372B0000U,
+0x372C0000U,
+0x372D0000U,
+0x372E0000U,
+0x372F0000U,
+0x37300000U,
+0x37310000U,
+0x37320000U,
+0x37330000U,
+0x37340000U,
+0x37350000U,
+0x37360000U,
+0x37370000U,
+0x37380000U,
+0x37390000U,
+0x373A0000U,
+0x373B0000U,
+0x373C0000U,
+0x373D0000U,
+0x373E0000U,
+0x373F0000U,
+0x37400000U,
+0x37410000U,
+0x37420000U,
+0x37430000U,
+0x37440000U,
+0x37450000U,
+0x37460000U,
+0x37470000U,
+0x37480000U,
+0x37490000U,
+0x374A0000U,
+0x374B0000U,
+0x374C0000U,
+0x374D0000U,
+0x374E0000U,
+0x374F0000U,
+0x37500000U,
+0x37510000U,
+0x37520000U,
+0x37530000U,
+0x37540000U,
+0x37550000U,
+0x37560000U,
+0x37570000U,
+0x37580000U,
+0x37590000U,
+0x375A0000U,
+0x375B0000U,
+0x375C0000U,
+0x375D0000U,
+0x375E0000U,
+0x375F0000U,
+0x37600000U,
+0x37610000U,
+0x37620000U,
+0x37630000U,
+0x37640000U,
+0x37650000U,
+0x37660000U,
+0x37670000U,
+0x37680000U,
+0x37690000U,
+0x376A0000U,
+0x376B0000U,
+0x376C0000U,
+0x376D0000U,
+0x376E0000U,
+0x376F0000U,
+0x37700000U,
+0x37710000U,
+0x37720000U,
+0x37730000U,
+0x37740000U,
+0x37750000U,
+0x37760000U,
+0x37770000U,
+0x37780000U,
+0x37790000U,
+0x377A0000U,
+0x377B0000U,
+0x377C0000U,
+0x377D0000U,
+0x377E0000U,
+0x377F0000U,
+0x37800000U,
+0x37808000U,
+0x37810000U,
+0x37818000U,
+0x37820000U,
+0x37828000U,
+0x37830000U,
+0x37838000U,
+0x37840000U,
+0x37848000U,
+0x37850000U,
+0x37858000U,
+0x37860000U,
+0x37868000U,
+0x37870000U,
+0x37878000U,
+0x37880000U,
+0x37888000U,
+0x37890000U,
+0x37898000U,
+0x378A0000U,
+0x378A8000U,
+0x378B0000U,
+0x378B8000U,
+0x378C0000U,
+0x378C8000U,
+0x378D0000U,
+0x378D8000U,
+0x378E0000U,
+0x378E8000U,
+0x378F0000U,
+0x378F8000U,
+0x37900000U,
+0x37908000U,
+0x37910000U,
+0x37918000U,
+0x37920000U,
+0x37928000U,
+0x37930000U,
+0x37938000U,
+0x37940000U,
+0x37948000U,
+0x37950000U,
+0x37958000U,
+0x37960000U,
+0x37968000U,
+0x37970000U,
+0x37978000U,
+0x37980000U,
+0x37988000U,
+0x37990000U,
+0x37998000U,
+0x379A0000U,
+0x379A8000U,
+0x379B0000U,
+0x379B8000U,
+0x379C0000U,
+0x379C8000U,
+0x379D0000U,
+0x379D8000U,
+0x379E0000U,
+0x379E8000U,
+0x379F0000U,
+0x379F8000U,
+0x37A00000U,
+0x37A08000U,
+0x37A10000U,
+0x37A18000U,
+0x37A20000U,
+0x37A28000U,
+0x37A30000U,
+0x37A38000U,
+0x37A40000U,
+0x37A48000U,
+0x37A50000U,
+0x37A58000U,
+0x37A60000U,
+0x37A68000U,
+0x37A70000U,
+0x37A78000U,
+0x37A80000U,
+0x37A88000U,
+0x37A90000U,
+0x37A98000U,
+0x37AA0000U,
+0x37AA8000U,
+0x37AB0000U,
+0x37AB8000U,
+0x37AC0000U,
+0x37AC8000U,
+0x37AD0000U,
+0x37AD8000U,
+0x37AE0000U,
+0x37AE8000U,
+0x37AF0000U,
+0x37AF8000U,
+0x37B00000U,
+0x37B08000U,
+0x37B10000U,
+0x37B18000U,
+0x37B20000U,
+0x37B28000U,
+0x37B30000U,
+0x37B38000U,
+0x37B40000U,
+0x37B48000U,
+0x37B50000U,
+0x37B58000U,
+0x37B60000U,
+0x37B68000U,
+0x37B70000U,
+0x37B78000U,
+0x37B80000U,
+0x37B88000U,
+0x37B90000U,
+0x37B98000U,
+0x37BA0000U,
+0x37BA8000U,
+0x37BB0000U,
+0x37BB8000U,
+0x37BC0000U,
+0x37BC8000U,
+0x37BD0000U,
+0x37BD8000U,
+0x37BE0000U,
+0x37BE8000U,
+0x37BF0000U,
+0x37BF8000U,
+0x37C00000U,
+0x37C08000U,
+0x37C10000U,
+0x37C18000U,
+0x37C20000U,
+0x37C28000U,
+0x37C30000U,
+0x37C38000U,
+0x37C40000U,
+0x37C48000U,
+0x37C50000U,
+0x37C58000U,
+0x37C60000U,
+0x37C68000U,
+0x37C70000U,
+0x37C78000U,
+0x37C80000U,
+0x37C88000U,
+0x37C90000U,
+0x37C98000U,
+0x37CA0000U,
+0x37CA8000U,
+0x37CB0000U,
+0x37CB8000U,
+0x37CC0000U,
+0x37CC8000U,
+0x37CD0000U,
+0x37CD8000U,
+0x37CE0000U,
+0x37CE8000U,
+0x37CF0000U,
+0x37CF8000U,
+0x37D00000U,
+0x37D08000U,
+0x37D10000U,
+0x37D18000U,
+0x37D20000U,
+0x37D28000U,
+0x37D30000U,
+0x37D38000U,
+0x37D40000U,
+0x37D48000U,
+0x37D50000U,
+0x37D58000U,
+0x37D60000U,
+0x37D68000U,
+0x37D70000U,
+0x37D78000U,
+0x37D80000U,
+0x37D88000U,
+0x37D90000U,
+0x37D98000U,
+0x37DA0000U,
+0x37DA8000U,
+0x37DB0000U,
+0x37DB8000U,
+0x37DC0000U,
+0x37DC8000U,
+0x37DD0000U,
+0x37DD8000U,
+0x37DE0000U,
+0x37DE8000U,
+0x37DF0000U,
+0x37DF8000U,
+0x37E00000U,
+0x37E08000U,
+0x37E10000U,
+0x37E18000U,
+0x37E20000U,
+0x37E28000U,
+0x37E30000U,
+0x37E38000U,
+0x37E40000U,
+0x37E48000U,
+0x37E50000U,
+0x37E58000U,
+0x37E60000U,
+0x37E68000U,
+0x37E70000U,
+0x37E78000U,
+0x37E80000U,
+0x37E88000U,
+0x37E90000U,
+0x37E98000U,
+0x37EA0000U,
+0x37EA8000U,
+0x37EB0000U,
+0x37EB8000U,
+0x37EC0000U,
+0x37EC8000U,
+0x37ED0000U,
+0x37ED8000U,
+0x37EE0000U,
+0x37EE8000U,
+0x37EF0000U,
+0x37EF8000U,
+0x37F00000U,
+0x37F08000U,
+0x37F10000U,
+0x37F18000U,
+0x37F20000U,
+0x37F28000U,
+0x37F30000U,
+0x37F38000U,
+0x37F40000U,
+0x37F48000U,
+0x37F50000U,
+0x37F58000U,
+0x37F60000U,
+0x37F68000U,
+0x37F70000U,
+0x37F78000U,
+0x37F80000U,
+0x37F88000U,
+0x37F90000U,
+0x37F98000U,
+0x37FA0000U,
+0x37FA8000U,
+0x37FB0000U,
+0x37FB8000U,
+0x37FC0000U,
+0x37FC8000U,
+0x37FD0000U,
+0x37FD8000U,
+0x37FE0000U,
+0x37FE8000U,
+0x37FF0000U,
+0x37FF8000U,
+0x38000000U,
+0x38004000U,
+0x38008000U,
+0x3800C000U,
+0x38010000U,
+0x38014000U,
+0x38018000U,
+0x3801C000U,
+0x38020000U,
+0x38024000U,
+0x38028000U,
+0x3802C000U,
+0x38030000U,
+0x38034000U,
+0x38038000U,
+0x3803C000U,
+0x38040000U,
+0x38044000U,
+0x38048000U,
+0x3804C000U,
+0x38050000U,
+0x38054000U,
+0x38058000U,
+0x3805C000U,
+0x38060000U,
+0x38064000U,
+0x38068000U,
+0x3806C000U,
+0x38070000U,
+0x38074000U,
+0x38078000U,
+0x3807C000U,
+0x38080000U,
+0x38084000U,
+0x38088000U,
+0x3808C000U,
+0x38090000U,
+0x38094000U,
+0x38098000U,
+0x3809C000U,
+0x380A0000U,
+0x380A4000U,
+0x380A8000U,
+0x380AC000U,
+0x380B0000U,
+0x380B4000U,
+0x380B8000U,
+0x380BC000U,
+0x380C0000U,
+0x380C4000U,
+0x380C8000U,
+0x380CC000U,
+0x380D0000U,
+0x380D4000U,
+0x380D8000U,
+0x380DC000U,
+0x380E0000U,
+0x380E4000U,
+0x380E8000U,
+0x380EC000U,
+0x380F0000U,
+0x380F4000U,
+0x380F8000U,
+0x380FC000U,
+0x38100000U,
+0x38104000U,
+0x38108000U,
+0x3810C000U,
+0x38110000U,
+0x38114000U,
+0x38118000U,
+0x3811C000U,
+0x38120000U,
+0x38124000U,
+0x38128000U,
+0x3812C000U,
+0x38130000U,
+0x38134000U,
+0x38138000U,
+0x3813C000U,
+0x38140000U,
+0x38144000U,
+0x38148000U,
+0x3814C000U,
+0x38150000U,
+0x38154000U,
+0x38158000U,
+0x3815C000U,
+0x38160000U,
+0x38164000U,
+0x38168000U,
+0x3816C000U,
+0x38170000U,
+0x38174000U,
+0x38178000U,
+0x3817C000U,
+0x38180000U,
+0x38184000U,
+0x38188000U,
+0x3818C000U,
+0x38190000U,
+0x38194000U,
+0x38198000U,
+0x3819C000U,
+0x381A0000U,
+0x381A4000U,
+0x381A8000U,
+0x381AC000U,
+0x381B0000U,
+0x381B4000U,
+0x381B8000U,
+0x381BC000U,
+0x381C0000U,
+0x381C4000U,
+0x381C8000U,
+0x381CC000U,
+0x381D0000U,
+0x381D4000U,
+0x381D8000U,
+0x381DC000U,
+0x381E0000U,
+0x381E4000U,
+0x381E8000U,
+0x381EC000U,
+0x381F0000U,
+0x381F4000U,
+0x381F8000U,
+0x381FC000U,
+0x38200000U,
+0x38204000U,
+0x38208000U,
+0x3820C000U,
+0x38210000U,
+0x38214000U,
+0x38218000U,
+0x3821C000U,
+0x38220000U,
+0x38224000U,
+0x38228000U,
+0x3822C000U,
+0x38230000U,
+0x38234000U,
+0x38238000U,
+0x3823C000U,
+0x38240000U,
+0x38244000U,
+0x38248000U,
+0x3824C000U,
+0x38250000U,
+0x38254000U,
+0x38258000U,
+0x3825C000U,
+0x38260000U,
+0x38264000U,
+0x38268000U,
+0x3826C000U,
+0x38270000U,
+0x38274000U,
+0x38278000U,
+0x3827C000U,
+0x38280000U,
+0x38284000U,
+0x38288000U,
+0x3828C000U,
+0x38290000U,
+0x38294000U,
+0x38298000U,
+0x3829C000U,
+0x382A0000U,
+0x382A4000U,
+0x382A8000U,
+0x382AC000U,
+0x382B0000U,
+0x382B4000U,
+0x382B8000U,
+0x382BC000U,
+0x382C0000U,
+0x382C4000U,
+0x382C8000U,
+0x382CC000U,
+0x382D0000U,
+0x382D4000U,
+0x382D8000U,
+0x382DC000U,
+0x382E0000U,
+0x382E4000U,
+0x382E8000U,
+0x382EC000U,
+0x382F0000U,
+0x382F4000U,
+0x382F8000U,
+0x382FC000U,
+0x38300000U,
+0x38304000U,
+0x38308000U,
+0x3830C000U,
+0x38310000U,
+0x38314000U,
+0x38318000U,
+0x3831C000U,
+0x38320000U,
+0x38324000U,
+0x38328000U,
+0x3832C000U,
+0x38330000U,
+0x38334000U,
+0x38338000U,
+0x3833C000U,
+0x38340000U,
+0x38344000U,
+0x38348000U,
+0x3834C000U,
+0x38350000U,
+0x38354000U,
+0x38358000U,
+0x3835C000U,
+0x38360000U,
+0x38364000U,
+0x38368000U,
+0x3836C000U,
+0x38370000U,
+0x38374000U,
+0x38378000U,
+0x3837C000U,
+0x38380000U,
+0x38384000U,
+0x38388000U,
+0x3838C000U,
+0x38390000U,
+0x38394000U,
+0x38398000U,
+0x3839C000U,
+0x383A0000U,
+0x383A4000U,
+0x383A8000U,
+0x383AC000U,
+0x383B0000U,
+0x383B4000U,
+0x383B8000U,
+0x383BC000U,
+0x383C0000U,
+0x383C4000U,
+0x383C8000U,
+0x383CC000U,
+0x383D0000U,
+0x383D4000U,
+0x383D8000U,
+0x383DC000U,
+0x383E0000U,
+0x383E4000U,
+0x383E8000U,
+0x383EC000U,
+0x383F0000U,
+0x383F4000U,
+0x383F8000U,
+0x383FC000U,
+0x38400000U,
+0x38404000U,
+0x38408000U,
+0x3840C000U,
+0x38410000U,
+0x38414000U,
+0x38418000U,
+0x3841C000U,
+0x38420000U,
+0x38424000U,
+0x38428000U,
+0x3842C000U,
+0x38430000U,
+0x38434000U,
+0x38438000U,
+0x3843C000U,
+0x38440000U,
+0x38444000U,
+0x38448000U,
+0x3844C000U,
+0x38450000U,
+0x38454000U,
+0x38458000U,
+0x3845C000U,
+0x38460000U,
+0x38464000U,
+0x38468000U,
+0x3846C000U,
+0x38470000U,
+0x38474000U,
+0x38478000U,
+0x3847C000U,
+0x38480000U,
+0x38484000U,
+0x38488000U,
+0x3848C000U,
+0x38490000U,
+0x38494000U,
+0x38498000U,
+0x3849C000U,
+0x384A0000U,
+0x384A4000U,
+0x384A8000U,
+0x384AC000U,
+0x384B0000U,
+0x384B4000U,
+0x384B8000U,
+0x384BC000U,
+0x384C0000U,
+0x384C4000U,
+0x384C8000U,
+0x384CC000U,
+0x384D0000U,
+0x384D4000U,
+0x384D8000U,
+0x384DC000U,
+0x384E0000U,
+0x384E4000U,
+0x384E8000U,
+0x384EC000U,
+0x384F0000U,
+0x384F4000U,
+0x384F8000U,
+0x384FC000U,
+0x38500000U,
+0x38504000U,
+0x38508000U,
+0x3850C000U,
+0x38510000U,
+0x38514000U,
+0x38518000U,
+0x3851C000U,
+0x38520000U,
+0x38524000U,
+0x38528000U,
+0x3852C000U,
+0x38530000U,
+0x38534000U,
+0x38538000U,
+0x3853C000U,
+0x38540000U,
+0x38544000U,
+0x38548000U,
+0x3854C000U,
+0x38550000U,
+0x38554000U,
+0x38558000U,
+0x3855C000U,
+0x38560000U,
+0x38564000U,
+0x38568000U,
+0x3856C000U,
+0x38570000U,
+0x38574000U,
+0x38578000U,
+0x3857C000U,
+0x38580000U,
+0x38584000U,
+0x38588000U,
+0x3858C000U,
+0x38590000U,
+0x38594000U,
+0x38598000U,
+0x3859C000U,
+0x385A0000U,
+0x385A4000U,
+0x385A8000U,
+0x385AC000U,
+0x385B0000U,
+0x385B4000U,
+0x385B8000U,
+0x385BC000U,
+0x385C0000U,
+0x385C4000U,
+0x385C8000U,
+0x385CC000U,
+0x385D0000U,
+0x385D4000U,
+0x385D8000U,
+0x385DC000U,
+0x385E0000U,
+0x385E4000U,
+0x385E8000U,
+0x385EC000U,
+0x385F0000U,
+0x385F4000U,
+0x385F8000U,
+0x385FC000U,
+0x38600000U,
+0x38604000U,
+0x38608000U,
+0x3860C000U,
+0x38610000U,
+0x38614000U,
+0x38618000U,
+0x3861C000U,
+0x38620000U,
+0x38624000U,
+0x38628000U,
+0x3862C000U,
+0x38630000U,
+0x38634000U,
+0x38638000U,
+0x3863C000U,
+0x38640000U,
+0x38644000U,
+0x38648000U,
+0x3864C000U,
+0x38650000U,
+0x38654000U,
+0x38658000U,
+0x3865C000U,
+0x38660000U,
+0x38664000U,
+0x38668000U,
+0x3866C000U,
+0x38670000U,
+0x38674000U,
+0x38678000U,
+0x3867C000U,
+0x38680000U,
+0x38684000U,
+0x38688000U,
+0x3868C000U,
+0x38690000U,
+0x38694000U,
+0x38698000U,
+0x3869C000U,
+0x386A0000U,
+0x386A4000U,
+0x386A8000U,
+0x386AC000U,
+0x386B0000U,
+0x386B4000U,
+0x386B8000U,
+0x386BC000U,
+0x386C0000U,
+0x386C4000U,
+0x386C8000U,
+0x386CC000U,
+0x386D0000U,
+0x386D4000U,
+0x386D8000U,
+0x386DC000U,
+0x386E0000U,
+0x386E4000U,
+0x386E8000U,
+0x386EC000U,
+0x386F0000U,
+0x386F4000U,
+0x386F8000U,
+0x386FC000U,
+0x38700000U,
+0x38704000U,
+0x38708000U,
+0x3870C000U,
+0x38710000U,
+0x38714000U,
+0x38718000U,
+0x3871C000U,
+0x38720000U,
+0x38724000U,
+0x38728000U,
+0x3872C000U,
+0x38730000U,
+0x38734000U,
+0x38738000U,
+0x3873C000U,
+0x38740000U,
+0x38744000U,
+0x38748000U,
+0x3874C000U,
+0x38750000U,
+0x38754000U,
+0x38758000U,
+0x3875C000U,
+0x38760000U,
+0x38764000U,
+0x38768000U,
+0x3876C000U,
+0x38770000U,
+0x38774000U,
+0x38778000U,
+0x3877C000U,
+0x38780000U,
+0x38784000U,
+0x38788000U,
+0x3878C000U,
+0x38790000U,
+0x38794000U,
+0x38798000U,
+0x3879C000U,
+0x387A0000U,
+0x387A4000U,
+0x387A8000U,
+0x387AC000U,
+0x387B0000U,
+0x387B4000U,
+0x387B8000U,
+0x387BC000U,
+0x387C0000U,
+0x387C4000U,
+0x387C8000U,
+0x387CC000U,
+0x387D0000U,
+0x387D4000U,
+0x387D8000U,
+0x387DC000U,
+0x387E0000U,
+0x387E4000U,
+0x387E8000U,
+0x387EC000U,
+0x387F0000U,
+0x387F4000U,
+0x387F8000U,
+0x387FC000U,
+0x38000000U,
+0x38002000U,
+0x38004000U,
+0x38006000U,
+0x38008000U,
+0x3800A000U,
+0x3800C000U,
+0x3800E000U,
+0x38010000U,
+0x38012000U,
+0x38014000U,
+0x38016000U,
+0x38018000U,
+0x3801A000U,
+0x3801C000U,
+0x3801E000U,
+0x38020000U,
+0x38022000U,
+0x38024000U,
+0x38026000U,
+0x38028000U,
+0x3802A000U,
+0x3802C000U,
+0x3802E000U,
+0x38030000U,
+0x38032000U,
+0x38034000U,
+0x38036000U,
+0x38038000U,
+0x3803A000U,
+0x3803C000U,
+0x3803E000U,
+0x38040000U,
+0x38042000U,
+0x38044000U,
+0x38046000U,
+0x38048000U,
+0x3804A000U,
+0x3804C000U,
+0x3804E000U,
+0x38050000U,
+0x38052000U,
+0x38054000U,
+0x38056000U,
+0x38058000U,
+0x3805A000U,
+0x3805C000U,
+0x3805E000U,
+0x38060000U,
+0x38062000U,
+0x38064000U,
+0x38066000U,
+0x38068000U,
+0x3806A000U,
+0x3806C000U,
+0x3806E000U,
+0x38070000U,
+0x38072000U,
+0x38074000U,
+0x38076000U,
+0x38078000U,
+0x3807A000U,
+0x3807C000U,
+0x3807E000U,
+0x38080000U,
+0x38082000U,
+0x38084000U,
+0x38086000U,
+0x38088000U,
+0x3808A000U,
+0x3808C000U,
+0x3808E000U,
+0x38090000U,
+0x38092000U,
+0x38094000U,
+0x38096000U,
+0x38098000U,
+0x3809A000U,
+0x3809C000U,
+0x3809E000U,
+0x380A0000U,
+0x380A2000U,
+0x380A4000U,
+0x380A6000U,
+0x380A8000U,
+0x380AA000U,
+0x380AC000U,
+0x380AE000U,
+0x380B0000U,
+0x380B2000U,
+0x380B4000U,
+0x380B6000U,
+0x380B8000U,
+0x380BA000U,
+0x380BC000U,
+0x380BE000U,
+0x380C0000U,
+0x380C2000U,
+0x380C4000U,
+0x380C6000U,
+0x380C8000U,
+0x380CA000U,
+0x380CC000U,
+0x380CE000U,
+0x380D0000U,
+0x380D2000U,
+0x380D4000U,
+0x380D6000U,
+0x380D8000U,
+0x380DA000U,
+0x380DC000U,
+0x380DE000U,
+0x380E0000U,
+0x380E2000U,
+0x380E4000U,
+0x380E6000U,
+0x380E8000U,
+0x380EA000U,
+0x380EC000U,
+0x380EE000U,
+0x380F0000U,
+0x380F2000U,
+0x380F4000U,
+0x380F6000U,
+0x380F8000U,
+0x380FA000U,
+0x380FC000U,
+0x380FE000U,
+0x38100000U,
+0x38102000U,
+0x38104000U,
+0x38106000U,
+0x38108000U,
+0x3810A000U,
+0x3810C000U,
+0x3810E000U,
+0x38110000U,
+0x38112000U,
+0x38114000U,
+0x38116000U,
+0x38118000U,
+0x3811A000U,
+0x3811C000U,
+0x3811E000U,
+0x38120000U,
+0x38122000U,
+0x38124000U,
+0x38126000U,
+0x38128000U,
+0x3812A000U,
+0x3812C000U,
+0x3812E000U,
+0x38130000U,
+0x38132000U,
+0x38134000U,
+0x38136000U,
+0x38138000U,
+0x3813A000U,
+0x3813C000U,
+0x3813E000U,
+0x38140000U,
+0x38142000U,
+0x38144000U,
+0x38146000U,
+0x38148000U,
+0x3814A000U,
+0x3814C000U,
+0x3814E000U,
+0x38150000U,
+0x38152000U,
+0x38154000U,
+0x38156000U,
+0x38158000U,
+0x3815A000U,
+0x3815C000U,
+0x3815E000U,
+0x38160000U,
+0x38162000U,
+0x38164000U,
+0x38166000U,
+0x38168000U,
+0x3816A000U,
+0x3816C000U,
+0x3816E000U,
+0x38170000U,
+0x38172000U,
+0x38174000U,
+0x38176000U,
+0x38178000U,
+0x3817A000U,
+0x3817C000U,
+0x3817E000U,
+0x38180000U,
+0x38182000U,
+0x38184000U,
+0x38186000U,
+0x38188000U,
+0x3818A000U,
+0x3818C000U,
+0x3818E000U,
+0x38190000U,
+0x38192000U,
+0x38194000U,
+0x38196000U,
+0x38198000U,
+0x3819A000U,
+0x3819C000U,
+0x3819E000U,
+0x381A0000U,
+0x381A2000U,
+0x381A4000U,
+0x381A6000U,
+0x381A8000U,
+0x381AA000U,
+0x381AC000U,
+0x381AE000U,
+0x381B0000U,
+0x381B2000U,
+0x381B4000U,
+0x381B6000U,
+0x381B8000U,
+0x381BA000U,
+0x381BC000U,
+0x381BE000U,
+0x381C0000U,
+0x381C2000U,
+0x381C4000U,
+0x381C6000U,
+0x381C8000U,
+0x381CA000U,
+0x381CC000U,
+0x381CE000U,
+0x381D0000U,
+0x381D2000U,
+0x381D4000U,
+0x381D6000U,
+0x381D8000U,
+0x381DA000U,
+0x381DC000U,
+0x381DE000U,
+0x381E0000U,
+0x381E2000U,
+0x381E4000U,
+0x381E6000U,
+0x381E8000U,
+0x381EA000U,
+0x381EC000U,
+0x381EE000U,
+0x381F0000U,
+0x381F2000U,
+0x381F4000U,
+0x381F6000U,
+0x381F8000U,
+0x381FA000U,
+0x381FC000U,
+0x381FE000U,
+0x38200000U,
+0x38202000U,
+0x38204000U,
+0x38206000U,
+0x38208000U,
+0x3820A000U,
+0x3820C000U,
+0x3820E000U,
+0x38210000U,
+0x38212000U,
+0x38214000U,
+0x38216000U,
+0x38218000U,
+0x3821A000U,
+0x3821C000U,
+0x3821E000U,
+0x38220000U,
+0x38222000U,
+0x38224000U,
+0x38226000U,
+0x38228000U,
+0x3822A000U,
+0x3822C000U,
+0x3822E000U,
+0x38230000U,
+0x38232000U,
+0x38234000U,
+0x38236000U,
+0x38238000U,
+0x3823A000U,
+0x3823C000U,
+0x3823E000U,
+0x38240000U,
+0x38242000U,
+0x38244000U,
+0x38246000U,
+0x38248000U,
+0x3824A000U,
+0x3824C000U,
+0x3824E000U,
+0x38250000U,
+0x38252000U,
+0x38254000U,
+0x38256000U,
+0x38258000U,
+0x3825A000U,
+0x3825C000U,
+0x3825E000U,
+0x38260000U,
+0x38262000U,
+0x38264000U,
+0x38266000U,
+0x38268000U,
+0x3826A000U,
+0x3826C000U,
+0x3826E000U,
+0x38270000U,
+0x38272000U,
+0x38274000U,
+0x38276000U,
+0x38278000U,
+0x3827A000U,
+0x3827C000U,
+0x3827E000U,
+0x38280000U,
+0x38282000U,
+0x38284000U,
+0x38286000U,
+0x38288000U,
+0x3828A000U,
+0x3828C000U,
+0x3828E000U,
+0x38290000U,
+0x38292000U,
+0x38294000U,
+0x38296000U,
+0x38298000U,
+0x3829A000U,
+0x3829C000U,
+0x3829E000U,
+0x382A0000U,
+0x382A2000U,
+0x382A4000U,
+0x382A6000U,
+0x382A8000U,
+0x382AA000U,
+0x382AC000U,
+0x382AE000U,
+0x382B0000U,
+0x382B2000U,
+0x382B4000U,
+0x382B6000U,
+0x382B8000U,
+0x382BA000U,
+0x382BC000U,
+0x382BE000U,
+0x382C0000U,
+0x382C2000U,
+0x382C4000U,
+0x382C6000U,
+0x382C8000U,
+0x382CA000U,
+0x382CC000U,
+0x382CE000U,
+0x382D0000U,
+0x382D2000U,
+0x382D4000U,
+0x382D6000U,
+0x382D8000U,
+0x382DA000U,
+0x382DC000U,
+0x382DE000U,
+0x382E0000U,
+0x382E2000U,
+0x382E4000U,
+0x382E6000U,
+0x382E8000U,
+0x382EA000U,
+0x382EC000U,
+0x382EE000U,
+0x382F0000U,
+0x382F2000U,
+0x382F4000U,
+0x382F6000U,
+0x382F8000U,
+0x382FA000U,
+0x382FC000U,
+0x382FE000U,
+0x38300000U,
+0x38302000U,
+0x38304000U,
+0x38306000U,
+0x38308000U,
+0x3830A000U,
+0x3830C000U,
+0x3830E000U,
+0x38310000U,
+0x38312000U,
+0x38314000U,
+0x38316000U,
+0x38318000U,
+0x3831A000U,
+0x3831C000U,
+0x3831E000U,
+0x38320000U,
+0x38322000U,
+0x38324000U,
+0x38326000U,
+0x38328000U,
+0x3832A000U,
+0x3832C000U,
+0x3832E000U,
+0x38330000U,
+0x38332000U,
+0x38334000U,
+0x38336000U,
+0x38338000U,
+0x3833A000U,
+0x3833C000U,
+0x3833E000U,
+0x38340000U,
+0x38342000U,
+0x38344000U,
+0x38346000U,
+0x38348000U,
+0x3834A000U,
+0x3834C000U,
+0x3834E000U,
+0x38350000U,
+0x38352000U,
+0x38354000U,
+0x38356000U,
+0x38358000U,
+0x3835A000U,
+0x3835C000U,
+0x3835E000U,
+0x38360000U,
+0x38362000U,
+0x38364000U,
+0x38366000U,
+0x38368000U,
+0x3836A000U,
+0x3836C000U,
+0x3836E000U,
+0x38370000U,
+0x38372000U,
+0x38374000U,
+0x38376000U,
+0x38378000U,
+0x3837A000U,
+0x3837C000U,
+0x3837E000U,
+0x38380000U,
+0x38382000U,
+0x38384000U,
+0x38386000U,
+0x38388000U,
+0x3838A000U,
+0x3838C000U,
+0x3838E000U,
+0x38390000U,
+0x38392000U,
+0x38394000U,
+0x38396000U,
+0x38398000U,
+0x3839A000U,
+0x3839C000U,
+0x3839E000U,
+0x383A0000U,
+0x383A2000U,
+0x383A4000U,
+0x383A6000U,
+0x383A8000U,
+0x383AA000U,
+0x383AC000U,
+0x383AE000U,
+0x383B0000U,
+0x383B2000U,
+0x383B4000U,
+0x383B6000U,
+0x383B8000U,
+0x383BA000U,
+0x383BC000U,
+0x383BE000U,
+0x383C0000U,
+0x383C2000U,
+0x383C4000U,
+0x383C6000U,
+0x383C8000U,
+0x383CA000U,
+0x383CC000U,
+0x383CE000U,
+0x383D0000U,
+0x383D2000U,
+0x383D4000U,
+0x383D6000U,
+0x383D8000U,
+0x383DA000U,
+0x383DC000U,
+0x383DE000U,
+0x383E0000U,
+0x383E2000U,
+0x383E4000U,
+0x383E6000U,
+0x383E8000U,
+0x383EA000U,
+0x383EC000U,
+0x383EE000U,
+0x383F0000U,
+0x383F2000U,
+0x383F4000U,
+0x383F6000U,
+0x383F8000U,
+0x383FA000U,
+0x383FC000U,
+0x383FE000U,
+0x38400000U,
+0x38402000U,
+0x38404000U,
+0x38406000U,
+0x38408000U,
+0x3840A000U,
+0x3840C000U,
+0x3840E000U,
+0x38410000U,
+0x38412000U,
+0x38414000U,
+0x38416000U,
+0x38418000U,
+0x3841A000U,
+0x3841C000U,
+0x3841E000U,
+0x38420000U,
+0x38422000U,
+0x38424000U,
+0x38426000U,
+0x38428000U,
+0x3842A000U,
+0x3842C000U,
+0x3842E000U,
+0x38430000U,
+0x38432000U,
+0x38434000U,
+0x38436000U,
+0x38438000U,
+0x3843A000U,
+0x3843C000U,
+0x3843E000U,
+0x38440000U,
+0x38442000U,
+0x38444000U,
+0x38446000U,
+0x38448000U,
+0x3844A000U,
+0x3844C000U,
+0x3844E000U,
+0x38450000U,
+0x38452000U,
+0x38454000U,
+0x38456000U,
+0x38458000U,
+0x3845A000U,
+0x3845C000U,
+0x3845E000U,
+0x38460000U,
+0x38462000U,
+0x38464000U,
+0x38466000U,
+0x38468000U,
+0x3846A000U,
+0x3846C000U,
+0x3846E000U,
+0x38470000U,
+0x38472000U,
+0x38474000U,
+0x38476000U,
+0x38478000U,
+0x3847A000U,
+0x3847C000U,
+0x3847E000U,
+0x38480000U,
+0x38482000U,
+0x38484000U,
+0x38486000U,
+0x38488000U,
+0x3848A000U,
+0x3848C000U,
+0x3848E000U,
+0x38490000U,
+0x38492000U,
+0x38494000U,
+0x38496000U,
+0x38498000U,
+0x3849A000U,
+0x3849C000U,
+0x3849E000U,
+0x384A0000U,
+0x384A2000U,
+0x384A4000U,
+0x384A6000U,
+0x384A8000U,
+0x384AA000U,
+0x384AC000U,
+0x384AE000U,
+0x384B0000U,
+0x384B2000U,
+0x384B4000U,
+0x384B6000U,
+0x384B8000U,
+0x384BA000U,
+0x384BC000U,
+0x384BE000U,
+0x384C0000U,
+0x384C2000U,
+0x384C4000U,
+0x384C6000U,
+0x384C8000U,
+0x384CA000U,
+0x384CC000U,
+0x384CE000U,
+0x384D0000U,
+0x384D2000U,
+0x384D4000U,
+0x384D6000U,
+0x384D8000U,
+0x384DA000U,
+0x384DC000U,
+0x384DE000U,
+0x384E0000U,
+0x384E2000U,
+0x384E4000U,
+0x384E6000U,
+0x384E8000U,
+0x384EA000U,
+0x384EC000U,
+0x384EE000U,
+0x384F0000U,
+0x384F2000U,
+0x384F4000U,
+0x384F6000U,
+0x384F8000U,
+0x384FA000U,
+0x384FC000U,
+0x384FE000U,
+0x38500000U,
+0x38502000U,
+0x38504000U,
+0x38506000U,
+0x38508000U,
+0x3850A000U,
+0x3850C000U,
+0x3850E000U,
+0x38510000U,
+0x38512000U,
+0x38514000U,
+0x38516000U,
+0x38518000U,
+0x3851A000U,
+0x3851C000U,
+0x3851E000U,
+0x38520000U,
+0x38522000U,
+0x38524000U,
+0x38526000U,
+0x38528000U,
+0x3852A000U,
+0x3852C000U,
+0x3852E000U,
+0x38530000U,
+0x38532000U,
+0x38534000U,
+0x38536000U,
+0x38538000U,
+0x3853A000U,
+0x3853C000U,
+0x3853E000U,
+0x38540000U,
+0x38542000U,
+0x38544000U,
+0x38546000U,
+0x38548000U,
+0x3854A000U,
+0x3854C000U,
+0x3854E000U,
+0x38550000U,
+0x38552000U,
+0x38554000U,
+0x38556000U,
+0x38558000U,
+0x3855A000U,
+0x3855C000U,
+0x3855E000U,
+0x38560000U,
+0x38562000U,
+0x38564000U,
+0x38566000U,
+0x38568000U,
+0x3856A000U,
+0x3856C000U,
+0x3856E000U,
+0x38570000U,
+0x38572000U,
+0x38574000U,
+0x38576000U,
+0x38578000U,
+0x3857A000U,
+0x3857C000U,
+0x3857E000U,
+0x38580000U,
+0x38582000U,
+0x38584000U,
+0x38586000U,
+0x38588000U,
+0x3858A000U,
+0x3858C000U,
+0x3858E000U,
+0x38590000U,
+0x38592000U,
+0x38594000U,
+0x38596000U,
+0x38598000U,
+0x3859A000U,
+0x3859C000U,
+0x3859E000U,
+0x385A0000U,
+0x385A2000U,
+0x385A4000U,
+0x385A6000U,
+0x385A8000U,
+0x385AA000U,
+0x385AC000U,
+0x385AE000U,
+0x385B0000U,
+0x385B2000U,
+0x385B4000U,
+0x385B6000U,
+0x385B8000U,
+0x385BA000U,
+0x385BC000U,
+0x385BE000U,
+0x385C0000U,
+0x385C2000U,
+0x385C4000U,
+0x385C6000U,
+0x385C8000U,
+0x385CA000U,
+0x385CC000U,
+0x385CE000U,
+0x385D0000U,
+0x385D2000U,
+0x385D4000U,
+0x385D6000U,
+0x385D8000U,
+0x385DA000U,
+0x385DC000U,
+0x385DE000U,
+0x385E0000U,
+0x385E2000U,
+0x385E4000U,
+0x385E6000U,
+0x385E8000U,
+0x385EA000U,
+0x385EC000U,
+0x385EE000U,
+0x385F0000U,
+0x385F2000U,
+0x385F4000U,
+0x385F6000U,
+0x385F8000U,
+0x385FA000U,
+0x385FC000U,
+0x385FE000U,
+0x38600000U,
+0x38602000U,
+0x38604000U,
+0x38606000U,
+0x38608000U,
+0x3860A000U,
+0x3860C000U,
+0x3860E000U,
+0x38610000U,
+0x38612000U,
+0x38614000U,
+0x38616000U,
+0x38618000U,
+0x3861A000U,
+0x3861C000U,
+0x3861E000U,
+0x38620000U,
+0x38622000U,
+0x38624000U,
+0x38626000U,
+0x38628000U,
+0x3862A000U,
+0x3862C000U,
+0x3862E000U,
+0x38630000U,
+0x38632000U,
+0x38634000U,
+0x38636000U,
+0x38638000U,
+0x3863A000U,
+0x3863C000U,
+0x3863E000U,
+0x38640000U,
+0x38642000U,
+0x38644000U,
+0x38646000U,
+0x38648000U,
+0x3864A000U,
+0x3864C000U,
+0x3864E000U,
+0x38650000U,
+0x38652000U,
+0x38654000U,
+0x38656000U,
+0x38658000U,
+0x3865A000U,
+0x3865C000U,
+0x3865E000U,
+0x38660000U,
+0x38662000U,
+0x38664000U,
+0x38666000U,
+0x38668000U,
+0x3866A000U,
+0x3866C000U,
+0x3866E000U,
+0x38670000U,
+0x38672000U,
+0x38674000U,
+0x38676000U,
+0x38678000U,
+0x3867A000U,
+0x3867C000U,
+0x3867E000U,
+0x38680000U,
+0x38682000U,
+0x38684000U,
+0x38686000U,
+0x38688000U,
+0x3868A000U,
+0x3868C000U,
+0x3868E000U,
+0x38690000U,
+0x38692000U,
+0x38694000U,
+0x38696000U,
+0x38698000U,
+0x3869A000U,
+0x3869C000U,
+0x3869E000U,
+0x386A0000U,
+0x386A2000U,
+0x386A4000U,
+0x386A6000U,
+0x386A8000U,
+0x386AA000U,
+0x386AC000U,
+0x386AE000U,
+0x386B0000U,
+0x386B2000U,
+0x386B4000U,
+0x386B6000U,
+0x386B8000U,
+0x386BA000U,
+0x386BC000U,
+0x386BE000U,
+0x386C0000U,
+0x386C2000U,
+0x386C4000U,
+0x386C6000U,
+0x386C8000U,
+0x386CA000U,
+0x386CC000U,
+0x386CE000U,
+0x386D0000U,
+0x386D2000U,
+0x386D4000U,
+0x386D6000U,
+0x386D8000U,
+0x386DA000U,
+0x386DC000U,
+0x386DE000U,
+0x386E0000U,
+0x386E2000U,
+0x386E4000U,
+0x386E6000U,
+0x386E8000U,
+0x386EA000U,
+0x386EC000U,
+0x386EE000U,
+0x386F0000U,
+0x386F2000U,
+0x386F4000U,
+0x386F6000U,
+0x386F8000U,
+0x386FA000U,
+0x386FC000U,
+0x386FE000U,
+0x38700000U,
+0x38702000U,
+0x38704000U,
+0x38706000U,
+0x38708000U,
+0x3870A000U,
+0x3870C000U,
+0x3870E000U,
+0x38710000U,
+0x38712000U,
+0x38714000U,
+0x38716000U,
+0x38718000U,
+0x3871A000U,
+0x3871C000U,
+0x3871E000U,
+0x38720000U,
+0x38722000U,
+0x38724000U,
+0x38726000U,
+0x38728000U,
+0x3872A000U,
+0x3872C000U,
+0x3872E000U,
+0x38730000U,
+0x38732000U,
+0x38734000U,
+0x38736000U,
+0x38738000U,
+0x3873A000U,
+0x3873C000U,
+0x3873E000U,
+0x38740000U,
+0x38742000U,
+0x38744000U,
+0x38746000U,
+0x38748000U,
+0x3874A000U,
+0x3874C000U,
+0x3874E000U,
+0x38750000U,
+0x38752000U,
+0x38754000U,
+0x38756000U,
+0x38758000U,
+0x3875A000U,
+0x3875C000U,
+0x3875E000U,
+0x38760000U,
+0x38762000U,
+0x38764000U,
+0x38766000U,
+0x38768000U,
+0x3876A000U,
+0x3876C000U,
+0x3876E000U,
+0x38770000U,
+0x38772000U,
+0x38774000U,
+0x38776000U,
+0x38778000U,
+0x3877A000U,
+0x3877C000U,
+0x3877E000U,
+0x38780000U,
+0x38782000U,
+0x38784000U,
+0x38786000U,
+0x38788000U,
+0x3878A000U,
+0x3878C000U,
+0x3878E000U,
+0x38790000U,
+0x38792000U,
+0x38794000U,
+0x38796000U,
+0x38798000U,
+0x3879A000U,
+0x3879C000U,
+0x3879E000U,
+0x387A0000U,
+0x387A2000U,
+0x387A4000U,
+0x387A6000U,
+0x387A8000U,
+0x387AA000U,
+0x387AC000U,
+0x387AE000U,
+0x387B0000U,
+0x387B2000U,
+0x387B4000U,
+0x387B6000U,
+0x387B8000U,
+0x387BA000U,
+0x387BC000U,
+0x387BE000U,
+0x387C0000U,
+0x387C2000U,
+0x387C4000U,
+0x387C6000U,
+0x387C8000U,
+0x387CA000U,
+0x387CC000U,
+0x387CE000U,
+0x387D0000U,
+0x387D2000U,
+0x387D4000U,
+0x387D6000U,
+0x387D8000U,
+0x387DA000U,
+0x387DC000U,
+0x387DE000U,
+0x387E0000U,
+0x387E2000U,
+0x387E4000U,
+0x387E6000U,
+0x387E8000U,
+0x387EA000U,
+0x387EC000U,
+0x387EE000U,
+0x387F0000U,
+0x387F2000U,
+0x387F4000U,
+0x387F6000U,
+0x387F8000U,
+0x387FA000U,
+0x387FC000U,
+0x387FE000U,
+};
+
+const quint32 qfloat16::exponenttable[64] = {
+0,
+0x800000U,
+0x1000000U,
+0x1800000U,
+0x2000000U,
+0x2800000U,
+0x3000000U,
+0x3800000U,
+0x4000000U,
+0x4800000U,
+0x5000000U,
+0x5800000U,
+0x6000000U,
+0x6800000U,
+0x7000000U,
+0x7800000U,
+0x8000000U,
+0x8800000U,
+0x9000000U,
+0x9800000U,
+0xA000000U,
+0xA800000U,
+0xB000000U,
+0xB800000U,
+0xC000000U,
+0xC800000U,
+0xD000000U,
+0xD800000U,
+0xE000000U,
+0xE800000U,
+0xF000000U,
+0x47800000U,
+0x80000000U,
+0x80800000U,
+0x81000000U,
+0x81800000U,
+0x82000000U,
+0x82800000U,
+0x83000000U,
+0x83800000U,
+0x84000000U,
+0x84800000U,
+0x85000000U,
+0x85800000U,
+0x86000000U,
+0x86800000U,
+0x87000000U,
+0x87800000U,
+0x88000000U,
+0x88800000U,
+0x89000000U,
+0x89800000U,
+0x8A000000U,
+0x8A800000U,
+0x8B000000U,
+0x8B800000U,
+0x8C000000U,
+0x8C800000U,
+0x8D000000U,
+0x8D800000U,
+0x8E000000U,
+0x8E800000U,
+0x8F000000U,
+0xC7800000U,
+};
+
+const quint32 qfloat16::offsettable[64] = {
+0,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+0,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+1024U,
+};
+
+const quint32 qfloat16::basetable[512] = {
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x0U,
+0x1U,
+0x2U,
+0x4U,
+0x8U,
+0x10U,
+0x20U,
+0x40U,
+0x80U,
+0x100U,
+0x200U,
+0x400U,
+0x800U,
+0xC00U,
+0x1000U,
+0x1400U,
+0x1800U,
+0x1C00U,
+0x2000U,
+0x2400U,
+0x2800U,
+0x2C00U,
+0x3000U,
+0x3400U,
+0x3800U,
+0x3C00U,
+0x4000U,
+0x4400U,
+0x4800U,
+0x4C00U,
+0x5000U,
+0x5400U,
+0x5800U,
+0x5C00U,
+0x6000U,
+0x6400U,
+0x6800U,
+0x6C00U,
+0x7000U,
+0x7400U,
+0x7800U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x7C00U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8000U,
+0x8001U,
+0x8002U,
+0x8004U,
+0x8008U,
+0x8010U,
+0x8020U,
+0x8040U,
+0x8080U,
+0x8100U,
+0x8200U,
+0x8400U,
+0x8800U,
+0x8C00U,
+0x9000U,
+0x9400U,
+0x9800U,
+0x9C00U,
+0xA000U,
+0xA400U,
+0xA800U,
+0xAC00U,
+0xB000U,
+0xB400U,
+0xB800U,
+0xBC00U,
+0xC000U,
+0xC400U,
+0xC800U,
+0xCC00U,
+0xD000U,
+0xD400U,
+0xD800U,
+0xDC00U,
+0xE000U,
+0xE400U,
+0xE800U,
+0xEC00U,
+0xF000U,
+0xF400U,
+0xF800U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+0xFC00U,
+};
+
+const quint32 qfloat16::shifttable[512] = {
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x17U,
+0x16U,
+0x15U,
+0x14U,
+0x13U,
+0x12U,
+0x11U,
+0x10U,
+0xFU,
+0xEU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0xDU,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x17U,
+0x16U,
+0x15U,
+0x14U,
+0x13U,
+0x12U,
+0x11U,
+0x10U,
+0xFU,
+0xEU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0xDU,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0x18U,
+0xDU,
+};
+
+#endif // !__F16C__ && !__ARM_FP16_FORMAT_IEEE
+
+QT_END_NAMESPACE
diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp
index be01e5a605..a8ed8ca6fd 100644
--- a/src/corelib/global/qglobal.cpp
+++ b/src/corelib/global/qglobal.cpp
@@ -1553,6 +1553,13 @@ bool qSharedBuild() noexcept
*/
/*!
+ \macro Q_OS_WASM
+ \relates <QtGlobal>
+
+ Defined on Web Assembly.
+*/
+
+/*!
\macro Q_CC_SYM
\relates <QtGlobal>
@@ -2945,6 +2952,7 @@ QString QSysInfo::machineHostName()
struct utsname u;
if (uname(&u) == 0)
return QString::fromLocal8Bit(u.nodename);
+ return QString();
#else
# ifdef Q_OS_WIN
// Important: QtNetwork depends on machineHostName() initializing ws2_32.dll
@@ -2957,7 +2965,6 @@ QString QSysInfo::machineHostName()
hostName[sizeof(hostName) - 1] = '\0';
return QString::fromLocal8Bit(hostName);
#endif
- return QString();
}
#endif // QT_BOOTSTRAPPED
@@ -3612,7 +3619,7 @@ bool qEnvironmentVariableIsSet(const char *varName) noexcept
(void)getenv_s(&requiredSize, 0, 0, varName);
return requiredSize != 0;
#else
- return ::getenv(varName) != 0;
+ return ::getenv(varName) != nullptr;
#endif
}
diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h
index 630a3265e2..f85824c7b3 100644
--- a/src/corelib/global/qglobal.h
+++ b/src/corelib/global/qglobal.h
@@ -1097,7 +1097,7 @@ for (auto _container_ = QtPrivate::qMakeForeachContainer(container); \
#endif
template <typename T> inline T *qGetPtrHelper(T *ptr) { return ptr; }
-template <typename Ptr> inline auto qGetPtrHelper(const Ptr &ptr) -> decltype(ptr.operator->()) { return ptr.operator->(); }
+template <typename Ptr> inline auto qGetPtrHelper(Ptr &ptr) -> decltype(ptr.operator->()) { return ptr.operator->(); }
// The body must be a statement:
#define Q_CAST_IGNORE_ALIGN(body) QT_WARNING_PUSH QT_WARNING_DISABLE_GCC("-Wcast-align") body QT_WARNING_POP
diff --git a/src/corelib/global/qglobalstatic.h b/src/corelib/global/qglobalstatic.h
index 4f89876793..7a7d65ed76 100644
--- a/src/corelib/global/qglobalstatic.h
+++ b/src/corelib/global/qglobalstatic.h
@@ -80,15 +80,15 @@ enum GuardValues {
{ \
struct HolderBase { \
~HolderBase() noexcept \
- { if (guard.load() == QtGlobalStatic::Initialized) \
- guard.store(QtGlobalStatic::Destroyed); } \
+ { if (guard.loadRelaxed() == QtGlobalStatic::Initialized) \
+ guard.storeRelaxed(QtGlobalStatic::Destroyed); } \
}; \
static struct Holder : public HolderBase { \
Type value; \
Holder() \
noexcept(noexcept(Type ARGS)) \
: value ARGS \
- { guard.store(QtGlobalStatic::Initialized); } \
+ { guard.storeRelaxed(QtGlobalStatic::Initialized); } \
} holder; \
return &holder.value; \
}
@@ -108,12 +108,12 @@ QT_BEGIN_NAMESPACE
int x = guard.loadAcquire(); \
if (Q_UNLIKELY(x >= QtGlobalStatic::Uninitialized)) { \
QMutexLocker locker(&mutex); \
- if (guard.load() == QtGlobalStatic::Uninitialized) { \
+ if (guard.loadRelaxed() == QtGlobalStatic::Uninitialized) { \
d = new Type ARGS; \
static struct Cleanup { \
~Cleanup() { \
delete d; \
- guard.store(QtGlobalStatic::Destroyed); \
+ guard.storeRelaxed(QtGlobalStatic::Destroyed); \
} \
} cleanup; \
guard.storeRelease(QtGlobalStatic::Initialized); \
@@ -129,8 +129,8 @@ struct QGlobalStatic
{
typedef T Type;
- bool isDestroyed() const { return guard.load() <= QtGlobalStatic::Destroyed; }
- bool exists() const { return guard.load() == QtGlobalStatic::Initialized; }
+ bool isDestroyed() const { return guard.loadRelaxed() <= QtGlobalStatic::Destroyed; }
+ bool exists() const { return guard.loadRelaxed() == QtGlobalStatic::Initialized; }
operator Type *() { if (isDestroyed()) return nullptr; return innerFunction(); }
Type *operator()() { if (isDestroyed()) return nullptr; return innerFunction(); }
Type *operator->()
diff --git a/src/corelib/global/qlibraryinfo.cpp b/src/corelib/global/qlibraryinfo.cpp
index c3eb469767..e82939dcd9 100644
--- a/src/corelib/global/qlibraryinfo.cpp
+++ b/src/corelib/global/qlibraryinfo.cpp
@@ -432,7 +432,24 @@ void QLibraryInfo::reload()
{
QLibraryInfoPrivate::reload();
}
-#endif
+
+void QLibraryInfo::sysrootify(QString *path)
+{
+ if (!QVariant::fromValue(rawLocation(SysrootifyPrefixPath, FinalPaths)).toBool())
+ return;
+
+ const QString sysroot = rawLocation(SysrootPath, FinalPaths);
+ if (sysroot.isEmpty())
+ return;
+
+ if (path->length() > 2 && path->at(1) == QLatin1Char(':')
+ && (path->at(2) == QLatin1Char('/') || path->at(2) == QLatin1Char('\\'))) {
+ path->replace(0, 2, sysroot); // Strip out the drive on Windows targets
+ } else {
+ path->prepend(sysroot);
+ }
+}
+#endif // QT_BUILD_QMAKE
/*!
Returns the location specified by \a loc.
@@ -444,18 +461,8 @@ QLibraryInfo::location(LibraryLocation loc)
QString ret = rawLocation(loc, FinalPaths);
// Automatically prepend the sysroot to target paths
- if (loc < SysrootPath || loc > LastHostPath) {
- QString sysroot = rawLocation(SysrootPath, FinalPaths);
- if (!sysroot.isEmpty()
- && QVariant::fromValue(rawLocation(SysrootifyPrefixPath, FinalPaths)).toBool()) {
- if (ret.length() > 2 && ret.at(1) == QLatin1Char(':')
- && (ret.at(2) == QLatin1Char('/') || ret.at(2) == QLatin1Char('\\'))) {
- ret.replace(0, 2, sysroot); // Strip out the drive on Windows targets
- } else {
- ret.prepend(sysroot);
- }
- }
- }
+ if (loc < SysrootPath || loc > LastHostPath)
+ sysrootify(&ret);
return ret;
}
@@ -598,6 +605,8 @@ QLibraryInfo::rawLocation(LibraryLocation loc, PathGroup group)
} else {
// we make any other path absolute to the prefix directory
baseDir = rawLocation(PrefixPath, group);
+ if (group == EffectivePaths)
+ sysrootify(&baseDir);
}
#else
if (loc == PrefixPath) {
diff --git a/src/corelib/global/qlibraryinfo.h b/src/corelib/global/qlibraryinfo.h
index 20fd3897f7..ed60b170a5 100644
--- a/src/corelib/global/qlibraryinfo.h
+++ b/src/corelib/global/qlibraryinfo.h
@@ -107,6 +107,7 @@ public:
enum PathGroup { FinalPaths, EffectivePaths, EffectiveSourcePaths, DevicePaths };
static QString rawLocation(LibraryLocation, PathGroup);
static void reload();
+ static void sysrootify(QString *path);
#endif
static QStringList platformPluginArguments(const QString &platformName);
diff --git a/src/corelib/global/qlogging.cpp b/src/corelib/global/qlogging.cpp
index 499ea3cdfb..f635a84e21 100644
--- a/src/corelib/global/qlogging.cpp
+++ b/src/corelib/global/qlogging.cpp
@@ -44,6 +44,7 @@
#include "qlogging_p.h"
#include "qlist.h"
#include "qbytearray.h"
+#include "qscopeguard.h"
#include "qstring.h"
#include "qvarlengtharray.h"
#include "qdebug.h"
@@ -158,6 +159,9 @@ static QT_PREPEND_NAMESPACE(qint64) qt_gettid()
#endif // !QT_BOOTSTRAPPED
#include <cstdlib>
+#include <algorithm>
+#include <memory>
+#include <vector>
#include <stdio.h>
@@ -194,7 +198,7 @@ static bool isFatal(QtMsgType msgType)
// it's fatal if the current value is exactly 1,
// otherwise decrement if it's non-zero
- return fatalCriticals.load() && fatalCriticals.fetchAndAddRelaxed(-1) == 1;
+ return fatalCriticals.loadRelaxed() && fatalCriticals.fetchAndAddRelaxed(-1) == 1;
}
if (msgType == QtWarningMsg || msgType == QtCriticalMsg) {
@@ -202,7 +206,7 @@ static bool isFatal(QtMsgType msgType)
// it's fatal if the current value is exactly 1,
// otherwise decrement if it's non-zero
- return fatalWarnings.load() && fatalWarnings.fetchAndAddRelaxed(-1) == 1;
+ return fatalWarnings.loadRelaxed() && fatalWarnings.fetchAndAddRelaxed(-1) == 1;
}
return false;
@@ -1076,8 +1080,8 @@ struct QMessagePattern {
void setPattern(const QString &pattern);
// 0 terminated arrays of literal tokens / literal or placeholder tokens
- const char **literals;
- const char **tokens;
+ std::unique_ptr<std::unique_ptr<const char[]>[]> literals;
+ std::unique_ptr<const char*[]> tokens;
QList<QString> timeArgs; // timeFormats in sequence of %{time
#ifndef QT_BOOTSTRAPPED
QElapsedTimer timer;
@@ -1100,9 +1104,6 @@ Q_DECLARE_TYPEINFO(QMessagePattern::BacktraceParams, Q_MOVABLE_TYPE);
QBasicMutex QMessagePattern::mutex;
QMessagePattern::QMessagePattern()
- : literals(0)
- , tokens(0)
- , fromEnvironment(false)
{
#ifndef QT_BOOTSTRAPPED
timer.start();
@@ -1110,6 +1111,7 @@ QMessagePattern::QMessagePattern()
const QString envPattern = QString::fromLocal8Bit(qgetenv("QT_MESSAGE_PATTERN"));
if (envPattern.isEmpty()) {
setPattern(QLatin1String(defaultPattern));
+ fromEnvironment = false;
} else {
setPattern(envPattern);
fromEnvironment = true;
@@ -1117,23 +1119,10 @@ QMessagePattern::QMessagePattern()
}
QMessagePattern::~QMessagePattern()
-{
- for (int i = 0; literals[i]; ++i)
- delete [] literals[i];
- delete [] literals;
- literals = 0;
- delete [] tokens;
- tokens = 0;
-}
+ = default;
void QMessagePattern::setPattern(const QString &pattern)
{
- if (literals) {
- for (int i = 0; literals[i]; ++i)
- delete [] literals[i];
- delete [] literals;
- }
- delete [] tokens;
timeArgs.clear();
#ifdef QLOGGING_HAVE_BACKTRACE
backtraceArgs.clear();
@@ -1171,9 +1160,9 @@ void QMessagePattern::setPattern(const QString &pattern)
lexemes.append(lexeme);
// tokenizer
- QVarLengthArray<const char*> literalsVar;
- tokens = new const char*[lexemes.size() + 1];
- tokens[lexemes.size()] = 0;
+ std::vector<std::unique_ptr<const char[]>> literalsVar;
+ tokens.reset(new const char*[lexemes.size() + 1]);
+ tokens[lexemes.size()] = nullptr;
bool nestedIfError = false;
bool inIf = false;
@@ -1267,7 +1256,7 @@ void QMessagePattern::setPattern(const QString &pattern)
char *literal = new char[lexeme.size() + 1];
strncpy(literal, lexeme.toLatin1().constData(), lexeme.size());
literal[lexeme.size()] = '\0';
- literalsVar.append(literal);
+ literalsVar.emplace_back(literal);
tokens[i] = literal;
}
}
@@ -1279,9 +1268,8 @@ void QMessagePattern::setPattern(const QString &pattern)
if (!error.isEmpty())
qt_message_print(error);
- literals = new const char*[literalsVar.size() + 1];
- literals[literalsVar.size()] = 0;
- memcpy(literals, literalsVar.constData(), literalsVar.size() * sizeof(const char*));
+ literals.reset(new std::unique_ptr<const char[]>[literalsVar.size() + 1]);
+ std::move(literalsVar.begin(), literalsVar.end(), &literals[0]);
}
#if defined(QLOGGING_HAVE_BACKTRACE) && !defined(QT_BOOTSTRAPPED)
@@ -1406,7 +1394,7 @@ QString qFormatLogMessage(QtMsgType type, const QMessageLogContext &context, con
#endif
// we do not convert file, function, line literals to local encoding due to overhead
- for (int i = 0; pattern->tokens[i] != 0; ++i) {
+ for (int i = 0; pattern->tokens[i]; ++i) {
const char *token = pattern->tokens[i];
if (token == endifTokenC) {
skip = false;
@@ -1512,9 +1500,9 @@ static void qDefaultMsgHandler(QtMsgType type, const char *buf);
static void qDefaultMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &buf);
// pointer to QtMsgHandler debug handler (without context)
-static QBasicAtomicPointer<void (QtMsgType, const char*)> msgHandler = Q_BASIC_ATOMIC_INITIALIZER(qDefaultMsgHandler);
+static QBasicAtomicPointer<void (QtMsgType, const char*)> msgHandler = Q_BASIC_ATOMIC_INITIALIZER(nullptr);
// pointer to QtMessageHandler debug handler (with context)
-static QBasicAtomicPointer<void (QtMsgType, const QMessageLogContext &, const QString &)> messageHandler = Q_BASIC_ATOMIC_INITIALIZER(qDefaultMessageHandler);
+static QBasicAtomicPointer<void (QtMsgType, const QMessageLogContext &, const QString &)> messageHandler = Q_BASIC_ATOMIC_INITIALIZER(nullptr);
// ------------------------ Alternate logging sinks -------------------------
@@ -1826,14 +1814,15 @@ static void qt_message_print(QtMsgType msgType, const QMessageLogContext &contex
// prevent recursion in case the message handler generates messages
// itself, e.g. by using Qt API
if (grabMessageHandler()) {
+ const auto ungrab = qScopeGuard([]{ ungrabMessageHandler(); });
+ auto oldStyle = msgHandler.loadAcquire();
+ auto newStye = messageHandler.loadAcquire();
// prefer new message handler over the old one
- if (msgHandler.load() == qDefaultMsgHandler
- || messageHandler.load() != qDefaultMessageHandler) {
- (*messageHandler.load())(msgType, context, message);
+ if (newStye || !oldStyle) {
+ (newStye ? newStye : qDefaultMessageHandler)(msgType, context, message);
} else {
- (*msgHandler.load())(msgType, message.toLocal8Bit().constData());
+ (oldStyle ? oldStyle : qDefaultMsgHandler)(msgType, message.toLocal8Bit().constData());
}
- ungrabMessageHandler();
} else {
fprintf(stderr, "%s\n", message.toLocal8Bit().constData());
}
@@ -2084,18 +2073,20 @@ void qErrnoWarning(int code, const char *msg, ...)
QtMessageHandler qInstallMessageHandler(QtMessageHandler h)
{
- if (!h)
- h = qDefaultMessageHandler;
- //set 'h' and return old message handler
- return messageHandler.fetchAndStoreRelaxed(h);
+ const auto old = messageHandler.fetchAndStoreOrdered(h);
+ if (old)
+ return old;
+ else
+ return qDefaultMessageHandler;
}
QtMsgHandler qInstallMsgHandler(QtMsgHandler h)
{
- if (!h)
- h = qDefaultMsgHandler;
- //set 'h' and return old message handler
- return msgHandler.fetchAndStoreRelaxed(h);
+ const auto old = msgHandler.fetchAndStoreOrdered(h);
+ if (old)
+ return old;
+ else
+ return qDefaultMsgHandler;
}
void qSetMessagePattern(const QString &pattern)
diff --git a/src/corelib/global/qmalloc.cpp b/src/corelib/global/qmalloc.cpp
index 05676a0da2..b071c1df62 100644
--- a/src/corelib/global/qmalloc.cpp
+++ b/src/corelib/global/qmalloc.cpp
@@ -74,18 +74,18 @@ void *qRealloc(void *ptr, size_t size)
void *qMallocAligned(size_t size, size_t alignment)
{
- return qReallocAligned(0, size, 0, alignment);
+ return qReallocAligned(nullptr, size, 0, alignment);
}
void *qReallocAligned(void *oldptr, size_t newsize, size_t oldsize, size_t alignment)
{
// fake an aligned allocation
- void *actualptr = oldptr ? static_cast<void **>(oldptr)[-1] : 0;
+ void *actualptr = oldptr ? static_cast<void **>(oldptr)[-1] : nullptr;
if (alignment <= sizeof(void*)) {
// special, fast case
void **newptr = static_cast<void **>(realloc(actualptr, newsize + sizeof(void*)));
if (!newptr)
- return 0;
+ return nullptr;
if (newptr == actualptr) {
// realloc succeeded without reallocating
return oldptr;
@@ -105,7 +105,7 @@ void *qReallocAligned(void *oldptr, size_t newsize, size_t oldsize, size_t align
void *real = realloc(actualptr, newsize + alignment);
if (!real)
- return 0;
+ return nullptr;
quintptr faked = reinterpret_cast<quintptr>(real) + alignment;
faked &= ~(alignment - 1);
diff --git a/src/corelib/global/qrandom.cpp b/src/corelib/global/qrandom.cpp
index 2ee8f0dd01..84cf960f2d 100644
--- a/src/corelib/global/qrandom.cpp
+++ b/src/corelib/global/qrandom.cpp
@@ -186,7 +186,7 @@ struct QRandomGenerator::SystemGenerator
#endif
static void closeDevice()
{
- int fd = self().fdp1.load() - 1;
+ int fd = self().fdp1.loadRelaxed() - 1;
if (fd >= 0)
qt_safe_close(fd);
}
@@ -310,7 +310,7 @@ static void fallback_fill(quint32 *ptr, qsizetype left) noexcept
*end++ = quint32(nsecs); // 5
#endif
- if (quint32 v = seed.load())
+ if (quint32 v = seed.loadRelaxed())
*end++ = v; // 6
#if QT_CONFIG(getauxval)
diff --git a/src/corelib/io/qabstractfileengine.cpp b/src/corelib/io/qabstractfileengine.cpp
index f2a895bbb8..8a1679c5af 100644
--- a/src/corelib/io/qabstractfileengine.cpp
+++ b/src/corelib/io/qabstractfileengine.cpp
@@ -159,7 +159,7 @@ QAbstractFileEngineHandler::~QAbstractFileEngineHandler()
*/
QAbstractFileEngine *qt_custom_file_engine_handler_create(const QString &path)
{
- QAbstractFileEngine *engine = 0;
+ QAbstractFileEngine *engine = nullptr;
if (qt_file_engine_handlers_in_use) {
QReadLocker locker(fileEngineHandlerMutex());
@@ -658,7 +658,7 @@ QStringList QAbstractFileEngine::entryList(QDir::Filters filters, const QStringL
QAbstractFileEngine::FileFlags QAbstractFileEngine::fileFlags(FileFlags type) const
{
Q_UNUSED(type);
- return 0;
+ return nullptr;
}
/*!
@@ -838,7 +838,7 @@ uchar *QAbstractFileEngine::map(qint64 offset, qint64 size, QFile::MemoryMapFlag
option.flags = flags;
MapExtensionReturn r;
if (!extension(MapExtension, &option, &r))
- return 0;
+ return nullptr;
return r.address;
}
@@ -1118,7 +1118,7 @@ QAbstractFileEngine::Iterator *QAbstractFileEngine::beginEntryList(QDir::Filters
{
Q_UNUSED(filters);
Q_UNUSED(filterNames);
- return 0;
+ return nullptr;
}
/*!
@@ -1126,7 +1126,7 @@ QAbstractFileEngine::Iterator *QAbstractFileEngine::beginEntryList(QDir::Filters
*/
QAbstractFileEngine::Iterator *QAbstractFileEngine::endEntryList()
{
- return 0;
+ return nullptr;
}
/*!
diff --git a/src/corelib/io/qbuffer.cpp b/src/corelib/io/qbuffer.cpp
index 7b3fa2ccad..8e980733de 100644
--- a/src/corelib/io/qbuffer.cpp
+++ b/src/corelib/io/qbuffer.cpp
@@ -50,7 +50,7 @@ class QBufferPrivate : public QIODevicePrivate
public:
QBufferPrivate()
- : buf(0)
+ : buf(nullptr)
#ifndef QT_NO_QOBJECT
, writtenSinceLastEmit(0), signalConnectionCount(0), signalsEmitted(false)
#endif
diff --git a/src/corelib/io/qdebug.cpp b/src/corelib/io/qdebug.cpp
index 6dc12cd83f..d13e94e096 100644
--- a/src/corelib/io/qdebug.cpp
+++ b/src/corelib/io/qdebug.cpp
@@ -147,7 +147,7 @@ using QtMiscUtils::fromHex;
// Has been defined in the header / inlined before Qt 5.4
QDebug::~QDebug()
{
- if (!--stream->ref) {
+ if (stream && !--stream->ref) {
if (stream->space && stream->buffer.endsWith(QLatin1Char(' ')))
stream->buffer.chop(1);
if (stream->message_output) {
@@ -843,36 +843,34 @@ QDebug &QDebug::resetFormat()
class QDebugStateSaverPrivate
{
public:
- QDebugStateSaverPrivate(QDebug &dbg)
- : m_dbg(dbg),
- m_spaces(dbg.autoInsertSpaces()),
- m_flags(0),
- m_streamParams(dbg.stream->ts.d_ptr->params)
+ QDebugStateSaverPrivate(QDebug::Stream *stream)
+ : m_stream(stream),
+ m_spaces(stream->space),
+ m_flags(stream->context.version > 1 ? stream->flags : 0),
+ m_streamParams(stream->ts.d_ptr->params)
{
- if (m_dbg.stream->context.version > 1)
- m_flags = m_dbg.stream->flags;
}
void restoreState()
{
- const bool currentSpaces = m_dbg.autoInsertSpaces();
+ const bool currentSpaces = m_stream->space;
if (currentSpaces && !m_spaces)
- if (m_dbg.stream->buffer.endsWith(QLatin1Char(' ')))
- m_dbg.stream->buffer.chop(1);
+ if (m_stream->buffer.endsWith(QLatin1Char(' ')))
+ m_stream->buffer.chop(1);
- m_dbg.setAutoInsertSpaces(m_spaces);
- m_dbg.stream->ts.d_ptr->params = m_streamParams;
- if (m_dbg.stream->context.version > 1)
- m_dbg.stream->flags = m_flags;
+ m_stream->space = m_spaces;
+ m_stream->ts.d_ptr->params = m_streamParams;
+ if (m_stream->context.version > 1)
+ m_stream->flags = m_flags;
if (!currentSpaces && m_spaces)
- m_dbg.stream->ts << ' ';
+ m_stream->ts << ' ';
}
- QDebug &m_dbg;
+ QDebug::Stream *m_stream;
// QDebug state
const bool m_spaces;
- int m_flags;
+ const int m_flags;
// QTextStream state
const QTextStreamPrivate::Params m_streamParams;
@@ -886,7 +884,7 @@ public:
\sa QDebug::setAutoInsertSpaces(), QDebug::autoInsertSpaces()
*/
QDebugStateSaver::QDebugStateSaver(QDebug &dbg)
- : d(new QDebugStateSaverPrivate(dbg))
+ : d(new QDebugStateSaverPrivate(dbg.stream))
{
}
diff --git a/src/corelib/io/qdebug.h b/src/corelib/io/qdebug.h
index 889fb6b571..421c5d933b 100644
--- a/src/corelib/io/qdebug.h
+++ b/src/corelib/io/qdebug.h
@@ -65,6 +65,7 @@ QT_BEGIN_NAMESPACE
class Q_CORE_EXPORT QDebug
{
friend class QMessageLogger;
+ friend class QDebugStateSaver;
friend class QDebugStateSaverPrivate;
struct Stream {
enum { VerbosityShift = 29, VerbosityMask = 0x7 };
@@ -114,7 +115,10 @@ public:
inline QDebug(QString *string) : stream(new Stream(string)) {}
inline QDebug(QtMsgType t) : stream(new Stream(t)) {}
inline QDebug(const QDebug &o):stream(o.stream) { ++stream->ref; }
+ QDebug(QDebug &&other) noexcept : stream{qExchange(other.stream, nullptr)} {}
inline QDebug &operator=(const QDebug &other);
+ QDebug &operator=(QDebug &&other) noexcept
+ { QDebug{std::move(other)}.swap(*this); return *this; }
~QDebug();
inline void swap(QDebug &other) noexcept { qSwap(stream, other.stream); }
@@ -203,10 +207,7 @@ public:
inline QDebug &QDebug::operator=(const QDebug &other)
{
- if (this != &other) {
- QDebug copy(other);
- qSwap(stream, copy.stream);
- }
+ QDebug{other}.swap(*this);
return *this;
}
diff --git a/src/corelib/io/qdir.cpp b/src/corelib/io/qdir.cpp
index 671913e92f..62dae3577c 100644
--- a/src/corelib/io/qdir.cpp
+++ b/src/corelib/io/qdir.cpp
@@ -1431,7 +1431,7 @@ QStringList QDir::entryList(const QStringList &nameFilters, Filters filters,
l.append(it.fileInfo());
}
QStringList ret;
- d->sortFileList(sort, l, &ret, 0);
+ d->sortFileList(sort, l, &ret, nullptr);
return ret;
}
@@ -1473,7 +1473,7 @@ QFileInfoList QDir::entryInfoList(const QStringList &nameFilters, Filters filter
l.append(it.fileInfo());
}
QFileInfoList ret;
- d->sortFileList(sort, l, 0, &ret);
+ d->sortFileList(sort, l, nullptr, &ret);
return ret;
}
diff --git a/src/corelib/io/qfile.cpp b/src/corelib/io/qfile.cpp
index 8d871904bc..9f9a9e3040 100644
--- a/src/corelib/io/qfile.cpp
+++ b/src/corelib/io/qfile.cpp
@@ -86,7 +86,7 @@ QFilePrivate::openExternalFile(int flags, int fd, QFile::FileHandleFlags handleF
return false;
#else
delete fileEngine;
- fileEngine = 0;
+ fileEngine = nullptr;
QFSFileEngine *fe = new QFSFileEngine;
fileEngine = fe;
return fe->open(QIODevice::OpenMode(flags), fd, handleFlags);
@@ -102,7 +102,7 @@ QFilePrivate::openExternalFile(int flags, FILE *fh, QFile::FileHandleFlags handl
return false;
#else
delete fileEngine;
- fileEngine = 0;
+ fileEngine = nullptr;
QFSFileEngine *fe = new QFSFileEngine;
fileEngine = fe;
return fe->open(QIODevice::OpenMode(flags), fh, handleFlags);
@@ -336,7 +336,7 @@ QFile::setFileName(const QString &name)
}
if(d->fileEngine) { //get a new file engine later
delete d->fileEngine;
- d->fileEngine = 0;
+ d->fileEngine = nullptr;
}
d->fileName = name;
}
@@ -810,7 +810,7 @@ QFile::copy(const QString &newName)
error = true;
d->setError(QFile::CopyError, tr("Cannot open %1 for input").arg(d->fileName));
} else {
- QString fileTemplate = QLatin1String("%1/qt_temp.XXXXXX");
+ const auto fileTemplate = QLatin1String("%1/qt_temp.XXXXXX");
#ifdef QT_NO_TEMPORARYFILE
QFile out(fileTemplate.arg(QFileInfo(newName).path()));
if (!out.open(QIODevice::ReadWrite))
diff --git a/src/corelib/io/qfiledevice.cpp b/src/corelib/io/qfiledevice.cpp
index 0689118f3e..2f74547054 100644
--- a/src/corelib/io/qfiledevice.cpp
+++ b/src/corelib/io/qfiledevice.cpp
@@ -53,7 +53,7 @@ QT_BEGIN_NAMESPACE
#endif
QFileDevicePrivate::QFileDevicePrivate()
- : fileEngine(0),
+ : fileEngine(nullptr),
cachedSize(0),
error(QFile::NoError), lastWasWrite(false)
{
@@ -63,7 +63,7 @@ QFileDevicePrivate::QFileDevicePrivate()
QFileDevicePrivate::~QFileDevicePrivate()
{
delete fileEngine;
- fileEngine = 0;
+ fileEngine = nullptr;
}
QAbstractFileEngine * QFileDevicePrivate::engine() const
diff --git a/src/corelib/io/qfileinfo.cpp b/src/corelib/io/qfileinfo.cpp
index 998382021d..f5b398feae 100644
--- a/src/corelib/io/qfileinfo.cpp
+++ b/src/corelib/io/qfileinfo.cpp
@@ -52,7 +52,7 @@ QString QFileInfoPrivate::getFileName(QAbstractFileEngine::FileName name) const
return fileNames[(int)name];
QString ret;
- if (fileEngine == 0) { // local file; use the QFileSystemEngine directly
+ if (fileEngine == nullptr) { // local file; use the QFileSystemEngine directly
switch (name) {
case QAbstractFileEngine::CanonicalName:
case QAbstractFileEngine::CanonicalPathName: {
@@ -103,7 +103,7 @@ QString QFileInfoPrivate::getFileOwner(QAbstractFileEngine::FileOwner own) const
if (cache_enabled && !fileOwners[(int)own].isNull())
return fileOwners[(int)own];
QString ret;
- if (fileEngine == 0) {
+ if (fileEngine == nullptr) {
switch (own) {
case QAbstractFileEngine::OwnerUser:
ret = QFileSystemEngine::resolveUserName(fileEntry, metaData);
@@ -134,7 +134,7 @@ uint QFileInfoPrivate::getFileFlags(QAbstractFileEngine::FileFlags request) cons
// extra syscall. Bundle detecton on Mac can be slow, expecially on network
// paths, so we separate out that as well.
- QAbstractFileEngine::FileFlags req = 0;
+ QAbstractFileEngine::FileFlags req = nullptr;
uint cachedFlags = 0;
if (request & (QAbstractFileEngine::FlagsMask | QAbstractFileEngine::TypesMask)) {
@@ -434,7 +434,7 @@ bool QFileInfo::operator==(const QFileInfo &fileinfo) const
return true;
Qt::CaseSensitivity sensitive;
- if (d->fileEngine == 0 || fileinfo.d_ptr->fileEngine == 0) {
+ if (d->fileEngine == nullptr || fileinfo.d_ptr->fileEngine == nullptr) {
if (d->fileEngine != fileinfo.d_ptr->fileEngine) // one is native, the other is a custom file-engine
return false;
@@ -649,7 +649,7 @@ bool QFileInfo::isRelative() const
Q_D(const QFileInfo);
if (d->isDefaultConstructed)
return true;
- if (d->fileEngine == 0)
+ if (d->fileEngine == nullptr)
return d->fileEntry.isRelative();
return d->fileEngine->isRelativePath();
}
@@ -682,7 +682,7 @@ bool QFileInfo::exists() const
Q_D(const QFileInfo);
if (d->isDefaultConstructed)
return false;
- if (d->fileEngine == 0) {
+ if (d->fileEngine == nullptr) {
if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::ExistsAttribute))
QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::ExistsAttribute);
return d->metaData.exists();
@@ -982,7 +982,7 @@ bool QFileInfo::isNativePath() const
Q_D(const QFileInfo);
if (d->isDefaultConstructed)
return false;
- if (d->fileEngine == 0)
+ if (d->fileEngine == nullptr)
return true;
return d->getFileFlags(QAbstractFileEngine::LocalDiskFlag);
}
@@ -1075,7 +1075,7 @@ bool QFileInfo::isRoot() const
Q_D(const QFileInfo);
if (d->isDefaultConstructed)
return false;
- if (d->fileEngine == 0) {
+ if (d->fileEngine == nullptr) {
if (d->fileEntry.isRoot()) {
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
//the path is a drive root, but the drive may not exist
diff --git a/src/corelib/io/qfileselector.cpp b/src/corelib/io/qfileselector.cpp
index 7f65b0f8f7..b1c2d84cdb 100644
--- a/src/corelib/io/qfileselector.cpp
+++ b/src/corelib/io/qfileselector.cpp
@@ -228,7 +228,18 @@ QUrl QFileSelector::select(const QUrl &filePath) const
QString selectedPath = d->select(equivalentPath);
ret.setPath(selectedPath.remove(0, scheme.size()));
} else {
+ // we need to store the original query and fragment, since toLocalFile() will strip it off
+ QString frag;
+ if (ret.hasFragment())
+ frag = ret.fragment();
+ QString query;
+ if (ret.hasQuery())
+ query= ret.query();
ret = QUrl::fromLocalFile(d->select(ret.toLocalFile()));
+ if (!frag.isNull())
+ ret.setFragment(frag);
+ if (!query.isNull())
+ ret.setQuery(query);
}
return ret;
}
diff --git a/src/corelib/io/qfilesystemengine.cpp b/src/corelib/io/qfilesystemengine.cpp
index 7abdf90bf5..21847c2a7c 100644
--- a/src/corelib/io/qfilesystemengine.cpp
+++ b/src/corelib/io/qfilesystemengine.cpp
@@ -124,7 +124,7 @@ static inline bool _q_checkEntry(QAbstractFileEngine *&engine, bool resolvingEnt
if (resolvingEntry) {
if (!(engine->fileFlags(QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::ExistsFlag)) {
delete engine;
- engine = 0;
+ engine = nullptr;
return false;
}
}
@@ -191,7 +191,7 @@ static bool _q_resolveEntryAndCreateLegacyEngine_recursive(QFileSystemEntry &ent
QAbstractFileEngine *QFileSystemEngine::resolveEntryAndCreateLegacyEngine(
QFileSystemEntry &entry, QFileSystemMetaData &data) {
QFileSystemEntry copy = entry;
- QAbstractFileEngine *engine = 0;
+ QAbstractFileEngine *engine = nullptr;
if (_q_resolveEntryAndCreateLegacyEngine_recursive(copy, data, engine))
// Reset entry to resolved copy.
diff --git a/src/corelib/io/qfilesystemengine_unix.cpp b/src/corelib/io/qfilesystemengine_unix.cpp
index b78e037865..066a7c3f01 100644
--- a/src/corelib/io/qfilesystemengine_unix.cpp
+++ b/src/corelib/io/qfilesystemengine_unix.cpp
@@ -813,7 +813,7 @@ QString QFileSystemEngine::resolveUserName(uint userId)
#endif
#if !defined(Q_OS_INTEGRITY) && !defined(Q_OS_WASM)
- struct passwd *pw = 0;
+ struct passwd *pw = nullptr;
#if QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) && !defined(Q_OS_VXWORKS)
struct passwd entry;
getpwuid_r(userId, &entry, buf.data(), buf.size(), &pw);
@@ -837,7 +837,7 @@ QString QFileSystemEngine::resolveGroupName(uint groupId)
#endif
#if !defined(Q_OS_INTEGRITY) && !defined(Q_OS_WASM)
- struct group *gr = 0;
+ struct group *gr = nullptr;
#if QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) && !defined(Q_OS_VXWORKS) && (!defined(Q_OS_ANDROID) || defined(Q_OS_ANDROID) && (__ANDROID_API__ >= 24))
size_max = sysconf(_SC_GETGR_R_SIZE_MAX);
if (size_max == -1)
diff --git a/src/corelib/io/qfilesystemiterator_unix.cpp b/src/corelib/io/qfilesystemiterator_unix.cpp
index a9acf542d4..5ac7e13a2e 100644
--- a/src/corelib/io/qfilesystemiterator_unix.cpp
+++ b/src/corelib/io/qfilesystemiterator_unix.cpp
@@ -50,15 +50,15 @@ QT_BEGIN_NAMESPACE
QFileSystemIterator::QFileSystemIterator(const QFileSystemEntry &entry, QDir::Filters filters,
const QStringList &nameFilters, QDirIterator::IteratorFlags flags)
: nativePath(entry.nativeFilePath())
- , dir(0)
- , dirEntry(0)
+ , dir(nullptr)
+ , dirEntry(nullptr)
, lastError(0)
{
Q_UNUSED(filters)
Q_UNUSED(nameFilters)
Q_UNUSED(flags)
- if ((dir = QT_OPENDIR(nativePath.constData())) == 0) {
+ if ((dir = QT_OPENDIR(nativePath.constData())) == nullptr) {
lastError = errno;
} else {
if (!nativePath.endsWith('/'))
diff --git a/src/corelib/io/qfilesystemwatcher_polling.cpp b/src/corelib/io/qfilesystemwatcher_polling.cpp
index a95a48cc8f..e07b02f7c2 100644
--- a/src/corelib/io/qfilesystemwatcher_polling.cpp
+++ b/src/corelib/io/qfilesystemwatcher_polling.cpp
@@ -110,33 +110,32 @@ QStringList QPollingFileSystemWatcherEngine::removePaths(const QStringList &path
void QPollingFileSystemWatcherEngine::timeout()
{
- QMutableHashIterator<QString, FileInfo> fit(files);
- while (fit.hasNext()) {
- QHash<QString, FileInfo>::iterator x = fit.next();
+ for (auto it = files.begin(), end = files.end(); it != end; /*erasing*/) {
+ auto x = it++;
QString path = x.key();
QFileInfo fi(path);
if (!fi.exists()) {
- fit.remove();
+ files.erase(x);
emit fileChanged(path, true);
} else if (x.value() != fi) {
x.value() = fi;
emit fileChanged(path, false);
}
}
- QMutableHashIterator<QString, FileInfo> dit(directories);
- while (dit.hasNext()) {
- QHash<QString, FileInfo>::iterator x = dit.next();
+
+ for (auto it = directories.begin(), end = directories.end(); it != end; /*erasing*/) {
+ auto x = it++;
QString path = x.key();
QFileInfo fi(path);
if (!path.endsWith(QLatin1Char('/')))
fi = QFileInfo(path + QLatin1Char('/'));
if (!fi.exists()) {
- dit.remove();
+ directories.erase(x);
emit directoryChanged(path, true);
} else if (x.value() != fi) {
fi.refresh();
if (!fi.exists()) {
- dit.remove();
+ directories.erase(x);
emit directoryChanged(path, true);
} else {
x.value() = fi;
diff --git a/src/corelib/io/qfilesystemwatcher_win.cpp b/src/corelib/io/qfilesystemwatcher_win.cpp
index c568369a06..bdbd3ba868 100644
--- a/src/corelib/io/qfilesystemwatcher_win.cpp
+++ b/src/corelib/io/qfilesystemwatcher_win.cpp
@@ -695,9 +695,8 @@ void QWindowsFileSystemWatcherEngineThread::run()
qErrnoWarning(error, "%ls", qUtf16Printable(msgFindNextFailed(h)));
}
- QMutableHashIterator<QFileSystemWatcherPathKey, QWindowsFileSystemWatcherEngine::PathInfo> it(h);
- while (it.hasNext()) {
- QWindowsFileSystemWatcherEngineThread::PathInfoHash::iterator x = it.next();
+ for (auto it = h.begin(), end = h.end(); it != end; /*erasing*/ ) {
+ auto x = it++;
QString absolutePath = x.value().absolutePath;
QFileInfo fileInfo(x.value().path);
DEBUG() << "checking" << x.key();
@@ -723,6 +722,7 @@ void QWindowsFileSystemWatcherEngineThread::run()
handleForDir.remove(QFileSystemWatcherPathKey(absolutePath));
// h is now invalid
+ break;
}
} else if (x.value().isDir) {
DEBUG() << x.key() << "directory changed!";
diff --git a/src/corelib/io/qfsfileengine.cpp b/src/corelib/io/qfsfileengine.cpp
index 387990ed79..0db27f3e25 100644
--- a/src/corelib/io/qfsfileengine.cpp
+++ b/src/corelib/io/qfsfileengine.cpp
@@ -127,7 +127,7 @@ void QFSFileEnginePrivate::init()
is_link = 0;
openMode = QIODevice::NotOpen;
fd = -1;
- fh = 0;
+ fh = nullptr;
lastIOCommand = IOFlushCommand;
lastFlushFailed = false;
closeFileHandle = false;
@@ -240,7 +240,7 @@ bool QFSFileEngine::open(QIODevice::OpenMode openMode)
d->openMode = openMode;
d->lastFlushFailed = false;
d->tried_stat = 0;
- d->fh = 0;
+ d->fh = nullptr;
d->fd = -1;
return d->nativeOpen(openMode);
@@ -299,7 +299,7 @@ bool QFSFileEnginePrivate::openFh(QIODevice::OpenMode openMode, FILE *fh)
QSystemError::stdString());
this->openMode = QIODevice::NotOpen;
- this->fh = 0;
+ this->fh = nullptr;
return false;
}
@@ -328,7 +328,7 @@ bool QFSFileEngine::open(QIODevice::OpenMode openMode, int fd, QFile::FileHandle
d->lastFlushFailed = false;
d->closeFileHandle = (handleFlags & QFile::AutoCloseHandle);
d->fileEntry.clear();
- d->fh = 0;
+ d->fh = nullptr;
d->fd = -1;
d->tried_stat = 0;
@@ -344,7 +344,7 @@ bool QFSFileEnginePrivate::openFd(QIODevice::OpenMode openMode, int fd)
{
Q_Q(QFSFileEngine);
this->fd = fd;
- fh = 0;
+ fh = nullptr;
// Seek to the end when in Append mode.
if (openMode & QFile::Append) {
@@ -405,7 +405,7 @@ bool QFSFileEnginePrivate::closeFdFh()
// We must reset these guys regardless; calling close again after a
// failed close causes crashes on some systems.
- fh = 0;
+ fh = nullptr;
fd = -1;
closed = (ret == 0);
}
@@ -822,7 +822,7 @@ QAbstractFileEngine::Iterator *QFSFileEngine::beginEntryList(QDir::Filters filte
*/
QAbstractFileEngine::Iterator *QFSFileEngine::endEntryList()
{
- return 0;
+ return nullptr;
}
#endif // QT_NO_FILESYSTEMITERATOR
@@ -870,7 +870,7 @@ bool QFSFileEngine::extension(Extension extension, const ExtensionOption *option
const MapExtensionOption *options = (const MapExtensionOption*)(option);
MapExtensionReturn *returnValue = static_cast<MapExtensionReturn*>(output);
returnValue->address = d->map(options->offset, options->size, options->flags);
- return (returnValue->address != 0);
+ return (returnValue->address != nullptr);
}
if (extension == UnMapExtension) {
const UnMapExtensionOption *options = (const UnMapExtensionOption*)option;
diff --git a/src/corelib/io/qfsfileengine_unix.cpp b/src/corelib/io/qfsfileengine_unix.cpp
index 90ad0126d6..d4983c72af 100644
--- a/src/corelib/io/qfsfileengine_unix.cpp
+++ b/src/corelib/io/qfsfileengine_unix.cpp
@@ -153,7 +153,7 @@ bool QFSFileEnginePrivate::nativeOpen(QIODevice::OpenMode openMode)
}
}
- fh = 0;
+ fh = nullptr;
}
closeFileHandle = true;
@@ -451,14 +451,14 @@ QAbstractFileEngine::FileFlags QFSFileEngine::fileFlags(FileFlags type) const
if (type & Refresh)
d->metaData.clear();
- QAbstractFileEngine::FileFlags ret = 0;
+ QAbstractFileEngine::FileFlags ret = { };
if (type & FlagsMask)
ret |= LocalDiskFlag;
bool exists;
{
- QFileSystemMetaData::MetaDataFlags queryFlags = 0;
+ QFileSystemMetaData::MetaDataFlags queryFlags = { };
queryFlags |= QFileSystemMetaData::MetaDataFlags(uint(type))
& QFileSystemMetaData::Permissions;
@@ -590,9 +590,9 @@ bool QFSFileEngine::setPermissions(uint perms)
QSystemError error;
bool ok;
if (d->fd != -1)
- ok = QFileSystemEngine::setPermissions(d->fd, QFile::Permissions(perms), error, 0);
+ ok = QFileSystemEngine::setPermissions(d->fd, QFile::Permissions(perms), error);
else
- ok = QFileSystemEngine::setPermissions(d->fileEntry, QFile::Permissions(perms), error, 0);
+ ok = QFileSystemEngine::setPermissions(d->fileEntry, QFile::Permissions(perms), error);
if (!ok) {
setError(QFile::PermissionsError, error.toString());
return false;
@@ -651,13 +651,13 @@ uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFla
Q_Q(QFSFileEngine);
if (openMode == QIODevice::NotOpen) {
q->setError(QFile::PermissionsError, qt_error_string(int(EACCES)));
- return 0;
+ return nullptr;
}
if (offset < 0 || offset > maxFileOffset
|| size < 0 || quint64(size) > quint64(size_t(-1))) {
q->setError(QFile::UnspecifiedError, qt_error_string(int(EINVAL)));
- return 0;
+ return nullptr;
}
// If we know the mapping will extend beyond EOF, fail early to avoid
@@ -685,14 +685,14 @@ uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFla
if (quint64(size + extra) > quint64((size_t)-1)) {
q->setError(QFile::UnspecifiedError, qt_error_string(int(EINVAL)));
- return 0;
+ return nullptr;
}
size_t realSize = (size_t)size + extra;
QT_OFF_T realOffset = QT_OFF_T(offset);
realOffset &= ~(QT_OFF_T(pageSize - 1));
- void *mapAddress = QT_MMAP((void*)0, realSize,
+ void *mapAddress = QT_MMAP((void*)nullptr, realSize,
access, sharemode, nativeHandle(), realOffset);
if (MAP_FAILED != mapAddress) {
uchar *address = extra + static_cast<uchar*>(mapAddress);
@@ -714,7 +714,7 @@ uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFla
q->setError(QFile::UnspecifiedError, qt_error_string(int(errno)));
break;
}
- return 0;
+ return nullptr;
}
bool QFSFileEnginePrivate::unmap(uchar *ptr)
diff --git a/src/corelib/io/qiodevice.cpp b/src/corelib/io/qiodevice.cpp
index 74df0f71ef..e26508e631 100644
--- a/src/corelib/io/qiodevice.cpp
+++ b/src/corelib/io/qiodevice.cpp
@@ -169,7 +169,7 @@ QIODevicePrivate::QIODevicePrivate()
, baseReadLineDataCalled(false)
, accessMode(Unset)
#ifdef QT_NO_QOBJECT
- , q_ptr(0)
+ , q_ptr(nullptr)
#endif
{
}
diff --git a/src/corelib/io/qipaddress.cpp b/src/corelib/io/qipaddress.cpp
index d9f7916dd4..b3421fca8f 100644
--- a/src/corelib/io/qipaddress.cpp
+++ b/src/corelib/io/qipaddress.cpp
@@ -67,7 +67,7 @@ static const QChar *checkedToAscii(Buffer &buffer, const QChar *begin, const QCh
*dst++ = *src++;
}
*dst = '\0';
- return 0;
+ return nullptr;
}
static bool parseIp4Internal(IPv4Address &address, const char *ptr, bool acceptLeadingZero);
@@ -175,7 +175,7 @@ const QChar *parseIp6(IPv6Address &address, const QChar *begin, const QChar *end
memset(address, 0, sizeof address);
if (colonCount == 2 && end - begin == 2) // "::"
- return 0;
+ return nullptr;
// if there's a double colon ("::"), this is how many zeroes it means
int zeroWordsToFill;
@@ -236,7 +236,7 @@ const QChar *parseIp6(IPv6Address &address, const QChar *begin, const QChar *end
address[13] = ip4 >> 16;
address[14] = ip4 >> 8;
address[15] = ip4;
- return 0;
+ return nullptr;
}
address[pos++] = x >> 8;
@@ -248,7 +248,7 @@ const QChar *parseIp6(IPv6Address &address, const QChar *begin, const QChar *end
return begin + (endptr - buffer.data());
ptr = endptr + 1;
}
- return pos == 16 ? 0 : end;
+ return pos == 16 ? nullptr : end;
}
static inline QChar toHex(uchar c)
@@ -256,7 +256,7 @@ static inline QChar toHex(uchar c)
return QChar::fromLatin1(QtMiscUtils::toHexLower(c));
}
-void toString(QString &appendTo, IPv6Address address)
+void toString(QString &appendTo, const IPv6Address address)
{
// the longest IPv6 address possible is:
// "1111:2222:3333:4444:5555:6666:255.255.255.255"
diff --git a/src/corelib/io/qipaddress_p.h b/src/corelib/io/qipaddress_p.h
index d95cccb3bd..ea31e5883d 100644
--- a/src/corelib/io/qipaddress_p.h
+++ b/src/corelib/io/qipaddress_p.h
@@ -64,7 +64,7 @@ typedef quint8 IPv6Address[16];
Q_CORE_EXPORT bool parseIp4(IPv4Address &address, const QChar *begin, const QChar *end);
Q_CORE_EXPORT const QChar *parseIp6(IPv6Address &address, const QChar *begin, const QChar *end);
Q_CORE_EXPORT void toString(QString &appendTo, IPv4Address address);
-Q_CORE_EXPORT void toString(QString &appendTo, IPv6Address address);
+Q_CORE_EXPORT void toString(QString &appendTo, const IPv6Address address);
} // namespace
diff --git a/src/corelib/io/qloggingcategory.cpp b/src/corelib/io/qloggingcategory.cpp
index 33253429a2..f6ff56c83c 100644
--- a/src/corelib/io/qloggingcategory.cpp
+++ b/src/corelib/io/qloggingcategory.cpp
@@ -214,8 +214,8 @@ static void setBoolLane(QBasicAtomicInt *atomic, bool enable, int shift)
Note that \a category must be kept valid during the lifetime of this object.
*/
QLoggingCategory::QLoggingCategory(const char *category)
- : d(0),
- name(0)
+ : d(nullptr),
+ name(nullptr)
{
init(category, QtDebugMsg);
}
@@ -231,15 +231,15 @@ QLoggingCategory::QLoggingCategory(const char *category)
\since 5.4
*/
QLoggingCategory::QLoggingCategory(const char *category, QtMsgType enableForLevel)
- : d(0),
- name(0)
+ : d(nullptr),
+ name(nullptr)
{
init(category, enableForLevel);
}
void QLoggingCategory::init(const char *category, QtMsgType severityLevel)
{
- enabled.store(0x01010101); // enabledDebug = enabledWarning = enabledCritical = true;
+ enabled.storeRelaxed(0x01010101); // enabledDebug = enabledWarning = enabledCritical = true;
if (category)
name = category;
@@ -342,10 +342,10 @@ void QLoggingCategory::setEnabled(QtMsgType type, bool enable)
{
switch (type) {
#ifdef Q_ATOMIC_INT8_IS_SUPPORTED
- case QtDebugMsg: bools.enabledDebug.store(enable); break;
- case QtInfoMsg: bools.enabledInfo.store(enable); break;
- case QtWarningMsg: bools.enabledWarning.store(enable); break;
- case QtCriticalMsg: bools.enabledCritical.store(enable); break;
+ case QtDebugMsg: bools.enabledDebug.storeRelaxed(enable); break;
+ case QtInfoMsg: bools.enabledInfo.storeRelaxed(enable); break;
+ case QtWarningMsg: bools.enabledWarning.storeRelaxed(enable); break;
+ case QtCriticalMsg: bools.enabledCritical.storeRelaxed(enable); break;
#else
case QtDebugMsg: setBoolLane(&enabled, enable, DebugShift); break;
case QtInfoMsg: setBoolLane(&enabled, enable, InfoShift); break;
diff --git a/src/corelib/io/qloggingcategory.h b/src/corelib/io/qloggingcategory.h
index 5825095729..1c3e10b493 100644
--- a/src/corelib/io/qloggingcategory.h
+++ b/src/corelib/io/qloggingcategory.h
@@ -58,15 +58,15 @@ public:
void setEnabled(QtMsgType type, bool enable);
#ifdef Q_ATOMIC_INT8_IS_SUPPORTED
- bool isDebugEnabled() const { return bools.enabledDebug.load(); }
- bool isInfoEnabled() const { return bools.enabledInfo.load(); }
- bool isWarningEnabled() const { return bools.enabledWarning.load(); }
- bool isCriticalEnabled() const { return bools.enabledCritical.load(); }
+ bool isDebugEnabled() const { return bools.enabledDebug.loadRelaxed(); }
+ bool isInfoEnabled() const { return bools.enabledInfo.loadRelaxed(); }
+ bool isWarningEnabled() const { return bools.enabledWarning.loadRelaxed(); }
+ bool isCriticalEnabled() const { return bools.enabledCritical.loadRelaxed(); }
#else
- bool isDebugEnabled() const { return enabled.load() >> DebugShift & 1; }
- bool isInfoEnabled() const { return enabled.load() >> InfoShift & 1; }
- bool isWarningEnabled() const { return enabled.load() >> WarningShift & 1; }
- bool isCriticalEnabled() const { return enabled.load() >> CriticalShift & 1; }
+ bool isDebugEnabled() const { return enabled.loadRelaxed() >> DebugShift & 1; }
+ bool isInfoEnabled() const { return enabled.loadRelaxed() >> InfoShift & 1; }
+ bool isWarningEnabled() const { return enabled.loadRelaxed() >> WarningShift & 1; }
+ bool isCriticalEnabled() const { return enabled.loadRelaxed() >> CriticalShift & 1; }
#endif
const char *categoryName() const { return name; }
diff --git a/src/corelib/io/qloggingregistry.cpp b/src/corelib/io/qloggingregistry.cpp
index 9792d956cc..7849dfd14c 100644
--- a/src/corelib/io/qloggingregistry.cpp
+++ b/src/corelib/io/qloggingregistry.cpp
@@ -165,7 +165,7 @@ void QLoggingRule::parse(const QStringRef &pattern)
p = QStringRef(p.string(), p.position() + 1, p.length() - 1);
}
if (p.contains(QLatin1Char('*'))) // '*' only supported at start/end
- flags = 0;
+ flags = PatternFlags();
}
category = p.toString();
@@ -415,7 +415,7 @@ QLoggingRegistry::installFilter(QLoggingCategory::CategoryFilter filter)
{
QMutexLocker locker(&registryMutex);
- if (filter == 0)
+ if (!filter)
filter = defaultCategoryFilter;
QLoggingCategory::CategoryFilter old = categoryFilter;
diff --git a/src/corelib/io/qprocess_p.h b/src/corelib/io/qprocess_p.h
index 00acb81158..d02e87837c 100644
--- a/src/corelib/io/qprocess_p.h
+++ b/src/corelib/io/qprocess_p.h
@@ -220,7 +220,7 @@ public:
template<> Q_INLINE_TEMPLATE void QSharedDataPointer<QProcessEnvironmentPrivate>::detach()
{
- if (d && d->ref.load() == 1)
+ if (d && d->ref.loadRelaxed() == 1)
return;
QProcessEnvironmentPrivate *x = (d ? new QProcessEnvironmentPrivate(*d)
: new QProcessEnvironmentPrivate);
diff --git a/src/corelib/io/qresource.cpp b/src/corelib/io/qresource.cpp
index e7d739b4dc..d143f7fda3 100644
--- a/src/corelib/io/qresource.cpp
+++ b/src/corelib/io/qresource.cpp
@@ -147,7 +147,7 @@ private:
public:
mutable QAtomicInt ref;
- inline QResourceRoot(): tree(0), names(0), payloads(0), version(0) {}
+ inline QResourceRoot(): tree(nullptr), names(nullptr), payloads(nullptr), version(0) {}
inline QResourceRoot(int version, const uchar *t, const uchar *n, const uchar *d) { setSource(version, t, n, d); }
virtual ~QResourceRoot() { }
int findNode(const QString &path, const QLocale &locale=QLocale()) const;
@@ -165,7 +165,7 @@ public:
quint64 lastModified(int node) const;
QStringList children(int node) const;
virtual QString mappingRoot() const { return QString(); }
- bool mappingRootSubdir(const QString &path, QString *match=0) const;
+ bool mappingRootSubdir(const QString &path, QString *match = nullptr) const;
inline bool operator==(const QResourceRoot &other) const
{ return tree == other.tree && names == other.names && payloads == other.payloads && version == other.version; }
inline bool operator!=(const QResourceRoot &other) const
@@ -320,7 +320,7 @@ QResourcePrivate::clear()
{
absoluteFilePath.clear();
compressionAlgo = QResource::NoCompression;
- data = 0;
+ data = nullptr;
size = 0;
children.clear();
lastModified = 0;
@@ -864,7 +864,7 @@ const uchar *QResourceRoot::data(int node, qint64 *size) const
{
if(node == -1) {
*size = 0;
- return 0;
+ return nullptr;
}
int offset = findOffset(node) + 4; //jump past name
@@ -881,7 +881,7 @@ const uchar *QResourceRoot::data(int node, qint64 *size) const
return ret;
}
*size = 0;
- return 0;
+ return nullptr;
}
quint64 QResourceRoot::lastModified(int node) const
@@ -991,7 +991,7 @@ class QDynamicBufferResourceRoot: public QResourceRoot
const uchar *buffer;
public:
- inline QDynamicBufferResourceRoot(const QString &_root) : root(_root), buffer(0) { }
+ inline QDynamicBufferResourceRoot(const QString &_root) : root(_root), buffer(nullptr) { }
inline ~QDynamicBufferResourceRoot() { }
inline const uchar *mappingBuffer() const { return buffer; }
QString mappingRoot() const override { return root; }
@@ -1063,12 +1063,14 @@ class QDynamicFileResourceRoot: public QDynamicBufferResourceRoot
qsizetype unmapLength;
public:
- inline QDynamicFileResourceRoot(const QString &_root) : QDynamicBufferResourceRoot(_root), unmapPointer(0), unmapLength(0) { }
+ QDynamicFileResourceRoot(const QString &_root)
+ : QDynamicBufferResourceRoot(_root), unmapPointer(nullptr), unmapLength(0)
+ { }
~QDynamicFileResourceRoot() {
#if defined(QT_USE_MMAP)
if (unmapPointer) {
munmap((char*)unmapPointer, unmapLength);
- unmapPointer = 0;
+ unmapPointer = nullptr;
unmapLength = 0;
} else
#endif
diff --git a/src/corelib/io/qsavefile.cpp b/src/corelib/io/qsavefile.cpp
index bde5133cb3..915d0a0a00 100644
--- a/src/corelib/io/qsavefile.cpp
+++ b/src/corelib/io/qsavefile.cpp
@@ -151,7 +151,7 @@ QSaveFile::~QSaveFile()
if (d->fileEngine) {
d->fileEngine->remove();
delete d->fileEngine;
- d->fileEngine = 0;
+ d->fileEngine = nullptr;
}
}
@@ -252,7 +252,7 @@ bool QSaveFile::open(OpenMode mode)
return true;
d->setError(d->fileEngine->error(), d->fileEngine->errorString());
delete d->fileEngine;
- d->fileEngine = 0;
+ d->fileEngine = nullptr;
} else {
QString msg =
QSaveFile::tr("QSaveFile cannot open '%1' without direct write fallback "
@@ -285,7 +285,7 @@ bool QSaveFile::open(OpenMode mode)
err = QFileDevice::OpenError;
d->setError(err, d->fileEngine->errorString());
delete d->fileEngine;
- d->fileEngine = 0;
+ d->fileEngine = nullptr;
return false;
}
@@ -339,7 +339,7 @@ bool QSaveFile::commit()
d->fileEngine->remove();
d->writeError = QFileDevice::NoError;
delete d->fileEngine;
- d->fileEngine = 0;
+ d->fileEngine = nullptr;
return false;
}
// atomically replace old file with new file
@@ -349,12 +349,12 @@ bool QSaveFile::commit()
d->setError(d->fileEngine->error(), d->fileEngine->errorString());
d->fileEngine->remove();
delete d->fileEngine;
- d->fileEngine = 0;
+ d->fileEngine = nullptr;
return false;
}
}
delete d->fileEngine;
- d->fileEngine = 0;
+ d->fileEngine = nullptr;
return true;
}
diff --git a/src/corelib/io/qstandardpaths_android.cpp b/src/corelib/io/qstandardpaths_android.cpp
index 83108e4387..1f4e0de1e7 100644
--- a/src/corelib/io/qstandardpaths_android.cpp
+++ b/src/corelib/io/qstandardpaths_android.cpp
@@ -136,7 +136,7 @@ static QString getExternalStoragePublicDirectory(const char *directoryField)
*/
static QString getExternalFilesDir(const char *directoryField = 0)
{
- QString &path = (*androidDirCache)[QString(QLatin1String("APPNAME_%1")).arg(QLatin1String(directoryField))];
+ QString &path = (*androidDirCache)[QLatin1String("APPNAME_%1").arg(QLatin1String(directoryField))];
if (!path.isEmpty())
return path;
diff --git a/src/corelib/io/qstorageinfo_unix.cpp b/src/corelib/io/qstorageinfo_unix.cpp
index d25a607d9f..b6413d7902 100644
--- a/src/corelib/io/qstorageinfo_unix.cpp
+++ b/src/corelib/io/qstorageinfo_unix.cpp
@@ -846,6 +846,9 @@ QList<QStorageInfo> QStorageInfoPrivate::mountedVolumes()
const QString mountDir = it.rootPath();
QStorageInfo info(mountDir);
+ info.d->device = it.device();
+ info.d->fileSystemType = it.fileSystemType();
+ info.d->subvolume = it.subvolume();
if (info.bytesTotal() == 0)
continue;
volumes.append(info);
diff --git a/src/corelib/io/qtemporaryfile.cpp b/src/corelib/io/qtemporaryfile.cpp
index ced08a9a87..c89ad06a1f 100644
--- a/src/corelib/io/qtemporaryfile.cpp
+++ b/src/corelib/io/qtemporaryfile.cpp
@@ -327,7 +327,7 @@ bool QTemporaryFileEngine::isReallyOpen() const
{
Q_D(const QFSFileEngine);
- if (!((0 == d->fh) && (-1 == d->fd)
+ if (!((nullptr == d->fh) && (-1 == d->fd)
#if defined Q_OS_WIN
&& (INVALID_HANDLE_VALUE == d->fileHandle)
#endif
@@ -902,7 +902,7 @@ QTemporaryFile *QTemporaryFile::createNativeFile(QFile &file)
{
if (QAbstractFileEngine *engine = file.d_func()->engine()) {
if(engine->fileFlags(QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::LocalDiskFlag)
- return 0; //native already
+ return nullptr; // native already
//cache
bool wasOpen = file.isOpen();
qint64 old_off = 0;
@@ -934,7 +934,7 @@ QTemporaryFile *QTemporaryFile::createNativeFile(QFile &file)
//done
return ret;
}
- return 0;
+ return nullptr;
}
/*!
diff --git a/src/corelib/io/qurl.cpp b/src/corelib/io/qurl.cpp
index 681a0c7ef2..d9ebc6c750 100644
--- a/src/corelib/io/qurl.cpp
+++ b/src/corelib/io/qurl.cpp
@@ -421,6 +421,7 @@
#include "private/qipaddress_p.h"
#include "qurlquery.h"
#include "private/qdir_p.h"
+#include <private/qmemory_p.h>
QT_BEGIN_NAMESPACE
@@ -520,10 +521,10 @@ public:
bool isEmpty() const
{ return sectionIsPresent == 0 && port == -1 && path.isEmpty(); }
- Error *cloneError() const;
+ std::unique_ptr<Error> cloneError() const;
void clearError();
void setError(ErrorCode errorCode, const QString &source, int supplement = -1);
- ErrorCode validityError(QString *source = 0, int *position = 0) const;
+ ErrorCode validityError(QString *source = nullptr, int *position = nullptr) const;
bool validateComponent(Section section, const QString &input, int begin, int end);
bool validateComponent(Section section, const QString &input)
{ return validateComponent(section, input, 0, uint(input.length())); }
@@ -576,7 +577,7 @@ public:
QString query;
QString fragment;
- Error *error;
+ std::unique_ptr<Error> error;
// not used for:
// - Port (port == -1 means absence)
@@ -591,7 +592,6 @@ public:
inline QUrlPrivate::QUrlPrivate()
: ref(1), port(-1),
- error(0),
sectionIsPresent(0),
flags(0)
{
@@ -613,19 +613,16 @@ inline QUrlPrivate::QUrlPrivate(const QUrlPrivate &copy)
}
inline QUrlPrivate::~QUrlPrivate()
-{
- delete error;
-}
+ = default;
-inline QUrlPrivate::Error *QUrlPrivate::cloneError() const
+std::unique_ptr<QUrlPrivate::Error> QUrlPrivate::cloneError() const
{
- return error ? new Error(*error) : 0;
+ return error ? qt_make_unique<Error>(*error) : nullptr;
}
inline void QUrlPrivate::clearError()
{
- delete error;
- error = 0;
+ error.reset();
}
inline void QUrlPrivate::setError(ErrorCode errorCode, const QString &source, int supplement)
@@ -634,7 +631,7 @@ inline void QUrlPrivate::setError(ErrorCode errorCode, const QString &source, in
// don't overwrite an error set in a previous section during parsing
return;
}
- error = new Error;
+ error = qt_make_unique<Error>();
error->code = errorCode;
error->source = source;
error->position = supplement;
@@ -826,7 +823,7 @@ recodeFromUser(const QString &input, const ushort *actions, int from, int to)
QString output;
const QChar *begin = input.constData() + from;
const QChar *end = input.constData() + to;
- if (qt_urlRecode(output, begin, end, 0, actions))
+ if (qt_urlRecode(output, begin, end, nullptr, actions))
return output;
return input.mid(from, to - from);
@@ -954,7 +951,7 @@ inline void QUrlPrivate::appendFragment(QString &appendTo, QUrl::FormattingOptio
{
appendToUser(appendTo, fragment, options,
options & QUrl::EncodeDelimiters ? fragmentInUrl :
- appendingTo == FullUrl ? 0 : fragmentInIsolation);
+ appendingTo == FullUrl ? nullptr : fragmentInIsolation);
}
inline void QUrlPrivate::appendQuery(QString &appendTo, QUrl::FormattingOptions options, Section appendingTo) const
@@ -1179,7 +1176,7 @@ inline void QUrlPrivate::appendHost(QString &appendTo, QUrl::FormattingOptions o
if (host.at(0).unicode() == '[') {
// IPv6 addresses might contain a zone-id which needs to be recoded
if (options != 0)
- if (qt_urlRecode(appendTo, host.constBegin(), host.constEnd(), options, 0))
+ if (qt_urlRecode(appendTo, host.constBegin(), host.constEnd(), options, nullptr))
return;
appendTo += host;
} else {
@@ -1221,7 +1218,7 @@ static const QChar *parseIpFuture(QString &host, const QChar *begin, const QChar
--end;
QString decoded;
- if (mode == QUrl::TolerantMode && qt_urlRecode(decoded, begin, end, QUrl::FullyDecoded, 0)) {
+ if (mode == QUrl::TolerantMode && qt_urlRecode(decoded, begin, end, QUrl::FullyDecoded, nullptr)) {
begin = decoded.constBegin();
end = decoded.constEnd();
}
@@ -1233,13 +1230,13 @@ static const QChar *parseIpFuture(QString &host, const QChar *begin, const QChar
host += *begin;
else if (begin->unicode() >= '0' && begin->unicode() <= '9')
host += *begin;
- else if (begin->unicode() < 0x80 && strchr(acceptable, begin->unicode()) != 0)
+ else if (begin->unicode() < 0x80 && strchr(acceptable, begin->unicode()) != nullptr)
host += *begin;
else
return decoded.isEmpty() ? begin : &origBegin[2];
}
host += QLatin1Char(']');
- return 0;
+ return nullptr;
}
return &origBegin[2];
}
@@ -1286,7 +1283,7 @@ static const QChar *parseIp6(QString &host, const QChar *begin, const QChar *end
host += zoneId;
}
host += QLatin1Char(']');
- return 0;
+ return nullptr;
}
inline bool QUrlPrivate::setHost(const QString &value, int from, int iend, QUrl::ParsingMode mode)
@@ -1352,7 +1349,7 @@ inline bool QUrlPrivate::setHost(const QString &value, int from, int iend, QUrl:
// check for percent-encoding first
QString s;
- if (mode == QUrl::TolerantMode && qt_urlRecode(s, begin, end, 0, 0)) {
+ if (mode == QUrl::TolerantMode && qt_urlRecode(s, begin, end, { }, nullptr)) {
// something was decoded
// anything encoded left?
int pos = s.indexOf(QChar(0x25)); // '%'
@@ -1837,7 +1834,7 @@ inline void QUrlPrivate::validate() const
\sa setUrl(), fromEncoded(), TolerantMode
*/
-QUrl::QUrl(const QString &url, ParsingMode parsingMode) : d(0)
+QUrl::QUrl(const QString &url, ParsingMode parsingMode) : d(nullptr)
{
setUrl(url, parsingMode);
}
@@ -1845,7 +1842,7 @@ QUrl::QUrl(const QString &url, ParsingMode parsingMode) : d(0)
/*!
Constructs an empty QUrl object.
*/
-QUrl::QUrl() : d(0)
+QUrl::QUrl() : d(nullptr)
{
}
@@ -1879,7 +1876,7 @@ QUrl::~QUrl()
bool QUrl::isValid() const
{
if (isEmpty()) {
- // also catches d == 0
+ // also catches d == nullptr
return false;
}
return d->validityError() == QUrlPrivate::NoError;
@@ -1907,7 +1904,7 @@ void QUrl::clear()
{
if (d && !d->ref.deref())
delete d;
- d = 0;
+ d = nullptr;
}
/*!
@@ -3805,7 +3802,7 @@ void QUrl::detach()
*/
bool QUrl::isDetached() const
{
- return !d || d->ref.load() == 1;
+ return !d || d->ref.loadRelaxed() == 1;
}
@@ -3994,21 +3991,21 @@ static QString errorMessage(QUrlPrivate::ErrorCode errorCode, const QString &err
return QString();
case QUrlPrivate::InvalidSchemeError: {
- QString msg = QStringLiteral("Invalid scheme (character '%1' not permitted)");
+ auto msg = QLatin1String("Invalid scheme (character '%1' not permitted)");
return msg.arg(c);
}
case QUrlPrivate::InvalidUserNameError:
- return QString(QStringLiteral("Invalid user name (character '%1' not permitted)"))
+ return QLatin1String("Invalid user name (character '%1' not permitted)")
.arg(c);
case QUrlPrivate::InvalidPasswordError:
- return QString(QStringLiteral("Invalid password (character '%1' not permitted)"))
+ return QLatin1String("Invalid password (character '%1' not permitted)")
.arg(c);
case QUrlPrivate::InvalidRegNameError:
if (errorPosition != -1)
- return QString(QStringLiteral("Invalid hostname (character '%1' not permitted)"))
+ return QLatin1String("Invalid hostname (character '%1' not permitted)")
.arg(c);
else
return QStringLiteral("Invalid hostname (contains invalid characters)");
@@ -4017,9 +4014,9 @@ static QString errorMessage(QUrlPrivate::ErrorCode errorCode, const QString &err
case QUrlPrivate::InvalidIPv6AddressError:
return QStringLiteral("Invalid IPv6 address");
case QUrlPrivate::InvalidCharacterInIPv6Error:
- return QStringLiteral("Invalid IPv6 address (character '%1' not permitted)").arg(c);
+ return QLatin1String("Invalid IPv6 address (character '%1' not permitted)").arg(c);
case QUrlPrivate::InvalidIPvFutureError:
- return QStringLiteral("Invalid IPvFuture address (character '%1' not permitted)").arg(c);
+ return QLatin1String("Invalid IPvFuture address (character '%1' not permitted)").arg(c);
case QUrlPrivate::HostMissingEndBracket:
return QStringLiteral("Expected ']' to match '[' in hostname");
@@ -4029,15 +4026,15 @@ static QString errorMessage(QUrlPrivate::ErrorCode errorCode, const QString &err
return QStringLiteral("Port field was empty");
case QUrlPrivate::InvalidPathError:
- return QString(QStringLiteral("Invalid path (character '%1' not permitted)"))
+ return QLatin1String("Invalid path (character '%1' not permitted)")
.arg(c);
case QUrlPrivate::InvalidQueryError:
- return QString(QStringLiteral("Invalid query (character '%1' not permitted)"))
+ return QLatin1String("Invalid query (character '%1' not permitted)")
.arg(c);
case QUrlPrivate::InvalidFragmentError:
- return QString(QStringLiteral("Invalid fragment (character '%1' not permitted)"))
+ return QLatin1String("Invalid fragment (character '%1' not permitted)")
.arg(c);
case QUrlPrivate::AuthorityPresentAndPathIsRelative:
@@ -4187,7 +4184,7 @@ static QUrl adjustFtpPath(QUrl url)
static bool isIp6(const QString &text)
{
QIPAddressUtils::IPv6Address address;
- return !text.isEmpty() && QIPAddressUtils::parseIp6(address, text.begin(), text.end()) == 0;
+ return !text.isEmpty() && QIPAddressUtils::parseIp6(address, text.begin(), text.end()) == nullptr;
}
/*!
diff --git a/src/corelib/io/qurlidna.cpp b/src/corelib/io/qurlidna.cpp
index 2305e66407..2f89d22660 100644
--- a/src/corelib/io/qurlidna.cpp
+++ b/src/corelib/io/qurlidna.cpp
@@ -1444,7 +1444,7 @@ static void mapToLowerCase(QString *str, int from)
{
int N = sizeof(NameprepCaseFolding) / sizeof(NameprepCaseFolding[0]);
- ushort *d = 0;
+ ushort *d = nullptr;
for (int i = from; i < str->size(); ++i) {
uint uc = str->at(i).unicode();
if (uc < 0x80) {
@@ -1474,7 +1474,7 @@ static void mapToLowerCase(QString *str, int from)
else
str->replace(--i, 2, reinterpret_cast<const QChar *>(&entry->mapping[0]), l);
i += l - 1;
- d = 0;
+ d = nullptr;
} else {
if (!d)
d = reinterpret_cast<ushort *>(str->data());
@@ -2404,7 +2404,7 @@ static const char * const idn_whitelist[] = {
};
static const size_t idn_whitelist_size = sizeof idn_whitelist / sizeof *idn_whitelist;
-static QStringList *user_idn_whitelist = 0;
+static QStringList *user_idn_whitelist = nullptr;
static bool lessThan(const QChar *a, int l, const char *c)
{
diff --git a/src/corelib/io/qurlquery.cpp b/src/corelib/io/qurlquery.cpp
index d76107abfd..36a2880bf1 100644
--- a/src/corelib/io/qurlquery.cpp
+++ b/src/corelib/io/qurlquery.cpp
@@ -189,7 +189,7 @@ public:
template<> void QSharedDataPointer<QUrlQueryPrivate>::detach()
{
- if (d && d->ref.load() == 1)
+ if (d && d->ref.loadRelaxed() == 1)
return;
QUrlQueryPrivate *x = (d ? new QUrlQueryPrivate(*d)
: new QUrlQueryPrivate);
@@ -262,7 +262,7 @@ inline QString QUrlQueryPrivate::recodeToUser(const QString &input, QUrl::Compon
if (!(encoding & QUrl::EncodeDelimiters)) {
QString output;
if (qt_urlRecode(output, input.constData(), input.constData() + input.length(),
- encoding, 0))
+ encoding, nullptr))
return output;
return input;
}
@@ -290,7 +290,7 @@ void QUrlQueryPrivate::setQuery(const QString &query)
const QChar *const end = pos + query.size();
while (pos != end) {
const QChar *begin = pos;
- const QChar *delimiter = 0;
+ const QChar *delimiter = nullptr;
while (pos != end) {
// scan for the component parts of this pair
if (!delimiter && pos->unicode() == valueDelimiter)
@@ -345,7 +345,7 @@ QSharedDataPointer<QUrlQueryPrivate>::clone()
\sa setQuery(), addQueryItem()
*/
QUrlQuery::QUrlQuery()
- : d(0)
+ : d(nullptr)
{
}
@@ -356,7 +356,7 @@ QUrlQuery::QUrlQuery()
set the query with setQuery().
*/
QUrlQuery::QUrlQuery(const QString &queryString)
- : d(queryString.isEmpty() ? 0 : new QUrlQueryPrivate(queryString))
+ : d(queryString.isEmpty() ? nullptr : new QUrlQueryPrivate(queryString))
{
}
@@ -369,7 +369,7 @@ QUrlQuery::QUrlQuery(const QString &queryString)
\sa QUrl::query()
*/
QUrlQuery::QUrlQuery(const QUrl &url)
- : d(0)
+ : d(nullptr)
{
// use internals to avoid unnecessary recoding
// ### FIXME: actually do it
@@ -462,7 +462,7 @@ bool QUrlQuery::isEmpty() const
*/
bool QUrlQuery::isDetached() const
{
- return d && d->ref.load() == 1;
+ return d && d->ref.loadRelaxed() == 1;
}
/*!
diff --git a/src/corelib/io/qurlrecode.cpp b/src/corelib/io/qurlrecode.cpp
index f23480c755..35ede8d078 100644
--- a/src/corelib/io/qurlrecode.cpp
+++ b/src/corelib/io/qurlrecode.cpp
@@ -377,7 +377,7 @@ static int recode(QString &result, const ushort *begin, const ushort *end, QUrl:
{
const int origSize = result.size();
const ushort *input = begin;
- ushort *output = 0;
+ ushort *output = nullptr;
EncodingAction action = EncodeCharacter;
for ( ; input != end; ++input) {
diff --git a/src/corelib/itemmodels/qabstractitemmodel.cpp b/src/corelib/itemmodels/qabstractitemmodel.cpp
index ad857612b6..2eacb3cc94 100644
--- a/src/corelib/itemmodels/qabstractitemmodel.cpp
+++ b/src/corelib/itemmodels/qabstractitemmodel.cpp
@@ -77,7 +77,7 @@ QPersistentModelIndexData *QPersistentModelIndexData::create(const QModelIndex &
void QPersistentModelIndexData::destroy(QPersistentModelIndexData *data)
{
Q_ASSERT(data);
- Q_ASSERT(data->ref.load() == 0);
+ Q_ASSERT(data->ref.loadRelaxed() == 0);
QAbstractItemModel *model = const_cast<QAbstractItemModel *>(data->index.model());
// a valid persistent model index with a null model pointer can only happen if the model was destroyed
if (model) {
diff --git a/src/corelib/kernel/qabstracteventdispatcher.cpp b/src/corelib/kernel/qabstracteventdispatcher.cpp
index 186c2e743b..7215b3f2bd 100644
--- a/src/corelib/kernel/qabstracteventdispatcher.cpp
+++ b/src/corelib/kernel/qabstracteventdispatcher.cpp
@@ -170,7 +170,7 @@ QAbstractEventDispatcher::~QAbstractEventDispatcher()
QAbstractEventDispatcher *QAbstractEventDispatcher::instance(QThread *thread)
{
QThreadData *data = thread ? QThreadData::get2(thread) : QThreadData::current();
- return data->eventDispatcher.load();
+ return data->eventDispatcher.loadRelaxed();
}
/*!
diff --git a/src/corelib/kernel/qbasictimer.cpp b/src/corelib/kernel/qbasictimer.cpp
index b16833b34c..ea8f8e2c77 100644
--- a/src/corelib/kernel/qbasictimer.cpp
+++ b/src/corelib/kernel/qbasictimer.cpp
@@ -105,6 +105,7 @@ QT_BEGIN_NAMESPACE
\sa stop(), isActive(), swap()
*/
+#if QT_DEPRECATED_SINCE(5, 14)
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
/*!
\internal
@@ -125,6 +126,7 @@ QBasicTimer &QBasicTimer::operator=(const QBasicTimer &other)
return *this;
}
#endif
+#endif
/*!
\fn QBasicTimer::~QBasicTimer()
diff --git a/src/corelib/kernel/qcore_mac_objc.mm b/src/corelib/kernel/qcore_mac_objc.mm
index 6b51eb65d9..15b69acbd4 100644
--- a/src/corelib/kernel/qcore_mac_objc.mm
+++ b/src/corelib/kernel/qcore_mac_objc.mm
@@ -514,5 +514,36 @@ Q_CONSTRUCTOR_FUNCTION(qt_apple_check_os_version);
// -------------------------------------------------------------------------
+void QMacKeyValueObserver::addObserver()
+{
+ [object addObserver:observer forKeyPath:keyPath
+ options:NSKeyValueObservingOptionNew context:callback.get()];
+}
+
+void QMacKeyValueObserver::removeObserver() {
+ if (object)
+ [object removeObserver:observer forKeyPath:keyPath context:callback.get()];
+ object = nil;
+}
+
+KeyValueObserver *QMacKeyValueObserver::observer = [[KeyValueObserver alloc] init];
+
+QT_END_NAMESPACE
+@implementation KeyValueObserver
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
+ change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context
+{
+ Q_UNUSED(keyPath);
+ Q_UNUSED(object);
+ Q_UNUSED(change);
+
+ (*reinterpret_cast<QMacKeyValueObserver::Callback*>(context))();
+}
+@end
+QT_BEGIN_NAMESPACE
+
+// -------------------------------------------------------------------------
+
+
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qcore_mac_p.h b/src/corelib/kernel/qcore_mac_p.h
index 920105ee9e..0e1a5fe345 100644
--- a/src/corelib/kernel/qcore_mac_p.h
+++ b/src/corelib/kernel/qcore_mac_p.h
@@ -67,6 +67,7 @@
#ifdef __OBJC__
#include <Foundation/Foundation.h>
+#include <functional>
#endif
#include "qstring.h"
@@ -295,13 +296,13 @@ QT_MAC_WEAK_IMPORT(_os_activity_current);
// -------------------------------------------------------------------------
#if defined( __OBJC__)
-class QMacScopedObserver
+class QMacNotificationObserver
{
public:
- QMacScopedObserver() {}
+ QMacNotificationObserver() {}
template<typename Functor>
- QMacScopedObserver(id object, NSNotificationName name, Functor callback) {
+ QMacNotificationObserver(id object, NSNotificationName name, Functor callback) {
observer = [[NSNotificationCenter defaultCenter] addObserverForName:name
object:object queue:nil usingBlock:^(NSNotification *) {
callback();
@@ -309,13 +310,13 @@ public:
];
}
- QMacScopedObserver(const QMacScopedObserver& other) = delete;
- QMacScopedObserver(QMacScopedObserver&& other) : observer(other.observer) {
+ QMacNotificationObserver(const QMacNotificationObserver& other) = delete;
+ QMacNotificationObserver(QMacNotificationObserver&& other) : observer(other.observer) {
other.observer = nil;
}
- QMacScopedObserver &operator=(const QMacScopedObserver& other) = delete;
- QMacScopedObserver &operator=(QMacScopedObserver&& other) {
+ QMacNotificationObserver &operator=(const QMacNotificationObserver& other) = delete;
+ QMacNotificationObserver &operator=(QMacNotificationObserver&& other) {
if (this != &other) {
remove();
observer = other.observer;
@@ -329,11 +330,65 @@ public:
[[NSNotificationCenter defaultCenter] removeObserver:observer];
observer = nil;
}
- ~QMacScopedObserver() { remove(); }
+ ~QMacNotificationObserver() { remove(); }
private:
id observer = nil;
};
+
+QT_END_NAMESPACE
+@interface QT_MANGLE_NAMESPACE(KeyValueObserver) : NSObject
+@end
+QT_NAMESPACE_ALIAS_OBJC_CLASS(KeyValueObserver);
+QT_BEGIN_NAMESPACE
+
+class Q_CORE_EXPORT QMacKeyValueObserver
+{
+public:
+ using Callback = std::function<void()>;
+
+ QMacKeyValueObserver() {}
+
+ // Note: QMacKeyValueObserver must not outlive the object observed!
+ QMacKeyValueObserver(id object, NSString *keyPath, Callback callback)
+ : object(object), keyPath(keyPath), callback(new Callback(callback)) { addObserver(); }
+
+ QMacKeyValueObserver(const QMacKeyValueObserver &other)
+ : QMacKeyValueObserver(other.object, other.keyPath, *other.callback.get()) {}
+
+ QMacKeyValueObserver(QMacKeyValueObserver &&other) { swap(other, *this); }
+
+ ~QMacKeyValueObserver() { removeObserver(); }
+
+ QMacKeyValueObserver &operator=(const QMacKeyValueObserver &other) {
+ QMacKeyValueObserver tmp(other);
+ swap(tmp, *this);
+ return *this;
+ }
+
+ QMacKeyValueObserver &operator=(QMacKeyValueObserver &&other) {
+ QMacKeyValueObserver tmp(std::move(other));
+ swap(tmp, *this);
+ return *this;
+ }
+
+ void removeObserver();
+
+private:
+ void swap(QMacKeyValueObserver &first, QMacKeyValueObserver &second) {
+ std::swap(first.object, second.object);
+ std::swap(first.keyPath, second.keyPath);
+ std::swap(first.callback, second.callback);
+ }
+
+ void addObserver();
+
+ id object = nil;
+ NSString *keyPath = nullptr;
+ std::unique_ptr<Callback> callback;
+
+ static KeyValueObserver *observer;
+};
#endif
// -------------------------------------------------------------------------
diff --git a/src/corelib/kernel/qcore_unix_p.h b/src/corelib/kernel/qcore_unix_p.h
index 7f58813535..b56c2b9732 100644
--- a/src/corelib/kernel/qcore_unix_p.h
+++ b/src/corelib/kernel/qcore_unix_p.h
@@ -164,7 +164,7 @@ inline void qt_ignore_sigpipe()
{
// Set to ignore SIGPIPE once only.
static QBasicAtomicInt atom = Q_BASIC_ATOMIC_INITIALIZER(0);
- if (!atom.load()) {
+ if (!atom.loadRelaxed()) {
// More than one thread could turn off SIGPIPE at the same time
// But that's acceptable because they all would be doing the same
// action
@@ -172,7 +172,7 @@ inline void qt_ignore_sigpipe()
memset(&noaction, 0, sizeof(noaction));
noaction.sa_handler = SIG_IGN;
::sigaction(SIGPIPE, &noaction, nullptr);
- atom.store(1);
+ atom.storeRelaxed(1);
}
}
diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp
index 865e16a3f0..9bc3fea23b 100644
--- a/src/corelib/kernel/qcoreapplication.cpp
+++ b/src/corelib/kernel/qcoreapplication.cpp
@@ -120,6 +120,7 @@
#ifdef Q_OS_WASM
#include <emscripten.h>
+#include <emscripten/val.h>
#endif
#ifdef QT_BOOTSTRAPPED
@@ -216,11 +217,11 @@ QString QCoreApplicationPrivate::appVersion() const
}
#endif
-QString *QCoreApplicationPrivate::cachedApplicationFilePath = 0;
+QString *QCoreApplicationPrivate::cachedApplicationFilePath = nullptr;
bool QCoreApplicationPrivate::checkInstance(const char *function)
{
- bool b = (QCoreApplication::self != 0);
+ bool b = (QCoreApplication::self != nullptr);
if (!b)
qWarning("QApplication::%s: Please instantiate the QApplication object first", function);
return b;
@@ -257,7 +258,7 @@ void QCoreApplicationPrivate::processCommandLineArguments()
}
if (j < argc) {
- argv[j] = 0;
+ argv[j] = nullptr;
argc = j;
}
}
@@ -367,11 +368,11 @@ Q_CORE_EXPORT uint qGlobalPostedEventsCount()
return currentThreadData->postEventList.size() - currentThreadData->postEventList.startOffset;
}
-QAbstractEventDispatcher *QCoreApplicationPrivate::eventDispatcher = 0;
+QAbstractEventDispatcher *QCoreApplicationPrivate::eventDispatcher = nullptr;
#endif // QT_NO_QOBJECT
-QCoreApplication *QCoreApplication::self = 0;
+QCoreApplication *QCoreApplication::self = nullptr;
uint QCoreApplicationPrivate::attribs =
(1 << Qt::AA_SynthesizeMouseForUnhandledTouchEvents) |
(1 << Qt::AA_SynthesizeMouseForUnhandledTabletEvents);
@@ -455,12 +456,12 @@ QCoreApplicationPrivate::QCoreApplicationPrivate(int &aargc, char **aargv, uint
, aboutToQuitEmitted(false)
, threadData_clean(false)
#else
- , q_ptr(0)
+ , q_ptr(nullptr)
#endif
{
app_compile_version = flags & 0xffffff;
static const char *const empty = "";
- if (argc == 0 || argv == 0) {
+ if (argc == 0 || argv == nullptr) {
argc = 0;
argv = const_cast<char **>(&empty);
}
@@ -551,8 +552,8 @@ void QCoreApplicationPrivate::eventDispatcherReady()
QBasicAtomicPointer<QThread> QCoreApplicationPrivate::theMainThread = Q_BASIC_ATOMIC_INITIALIZER(0);
QThread *QCoreApplicationPrivate::mainThread()
{
- Q_ASSERT(theMainThread.load() != 0);
- return theMainThread.load();
+ Q_ASSERT(theMainThread.loadRelaxed() != 0);
+ return theMainThread.loadRelaxed();
}
bool QCoreApplicationPrivate::threadRequiresCoreApplication()
@@ -799,6 +800,10 @@ void QCoreApplicationPrivate::init()
Module.print(err);
});
);
+
+#if QT_CONFIG(thread)
+ QThreadPrivate::idealThreadCount = emscripten::val::global("navigator")["hardwareConcurrency"].as<int>();
+#endif
#endif
// Store app name/version (so they're still available after QCoreApplication is destroyed)
@@ -849,7 +854,7 @@ void QCoreApplicationPrivate::init()
#ifndef QT_NO_QOBJECT
// use the event dispatcher created by the app programmer (if any)
Q_ASSERT(!eventDispatcher);
- eventDispatcher = threadData->eventDispatcher.load();
+ eventDispatcher = threadData->eventDispatcher.loadRelaxed();
// otherwise we create one
if (!eventDispatcher)
@@ -886,7 +891,7 @@ QCoreApplication::~QCoreApplication()
{
qt_call_post_routines();
- self = 0;
+ self = nullptr;
#ifndef QT_NO_QOBJECT
QCoreApplicationPrivate::is_app_closing = true;
QCoreApplicationPrivate::is_app_running = false;
@@ -894,7 +899,7 @@ QCoreApplication::~QCoreApplication()
#if QT_CONFIG(thread)
// Synchronize and stop the global thread pool threads.
- QThreadPool *globalThreadPool = 0;
+ QThreadPool *globalThreadPool = nullptr;
QT_TRY {
globalThreadPool = QThreadPool::globalInstance();
} QT_CATCH (...) {
@@ -905,10 +910,10 @@ QCoreApplication::~QCoreApplication()
#endif
#ifndef QT_NO_QOBJECT
- d_func()->threadData->eventDispatcher = 0;
+ d_func()->threadData->eventDispatcher = nullptr;
if (QCoreApplicationPrivate::eventDispatcher)
QCoreApplicationPrivate::eventDispatcher->closingDown();
- QCoreApplicationPrivate::eventDispatcher = 0;
+ QCoreApplicationPrivate::eventDispatcher = nullptr;
#endif
#if QT_CONFIG(library)
@@ -972,7 +977,11 @@ void QCoreApplication::setAttribute(Qt::ApplicationAttribute attribute, bool on)
QCoreApplicationPrivate::attribs |= 1 << attribute;
else
QCoreApplicationPrivate::attribs &= ~(1 << attribute);
+#if defined(QT_NO_QOBJECT)
if (Q_UNLIKELY(qApp)) {
+#else
+ if (Q_UNLIKELY(QCoreApplicationPrivate::is_app_running)) {
+#endif
switch (attribute) {
case Qt::AA_EnableHighDpiScaling:
case Qt::AA_DisableHighDpiScaling:
@@ -1297,7 +1306,7 @@ void QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags)
QThreadData *data = QThreadData::current();
if (!data->hasEventDispatcher())
return;
- data->eventDispatcher.load()->processEvents(flags);
+ data->eventDispatcher.loadRelaxed()->processEvents(flags);
}
/*!
@@ -1329,7 +1338,7 @@ void QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags, int m
return;
QElapsedTimer start;
start.start();
- while (data->eventDispatcher.load()->processEvents(flags & ~QEventLoop::WaitForMoreEvents)) {
+ while (data->eventDispatcher.loadRelaxed()->processEvents(flags & ~QEventLoop::WaitForMoreEvents)) {
if (start.elapsed() > ms)
break;
}
@@ -1733,7 +1742,7 @@ void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type
--data->postEventList.recursion;
if (!data->postEventList.recursion && !data->canWait && data->hasEventDispatcher())
- data->eventDispatcher.load()->wakeUp();
+ data->eventDispatcher.loadRelaxed()->wakeUp();
// clear the global list, i.e. remove everything that was
// delivered.
@@ -1984,7 +1993,7 @@ void QCoreApplicationPrivate::deref()
void QCoreApplicationPrivate::maybeQuit()
{
- if (quitLockRef.load() == 0 && in_exec && quitLockRefEnabled && shouldQuit())
+ if (quitLockRef.loadRelaxed() == 0 && in_exec && quitLockRefEnabled && shouldQuit())
QCoreApplication::postEvent(QCoreApplication::instance(), new QEvent(QEvent::Quit));
}
@@ -2953,7 +2962,7 @@ bool QCoreApplication::hasPendingEvents()
QAbstractEventDispatcher *QCoreApplication::eventDispatcher()
{
if (QCoreApplicationPrivate::theMainThread)
- return QCoreApplicationPrivate::theMainThread.load()->eventDispatcher();
+ return QCoreApplicationPrivate::theMainThread.loadRelaxed()->eventDispatcher();
return 0;
}
diff --git a/src/corelib/kernel/qcoreapplication_win.cpp b/src/corelib/kernel/qcoreapplication_win.cpp
index 6995f9bbab..3f22c309ff 100644
--- a/src/corelib/kernel/qcoreapplication_win.cpp
+++ b/src/corelib/kernel/qcoreapplication_win.cpp
@@ -688,6 +688,12 @@ static const char *winPosInsertAfter(quintptr h)
static const char *sessionMgrLogOffOption(uint p)
{
+#ifndef ENDSESSION_CLOSEAPP
+#define ENDSESSION_CLOSEAPP 0x00000001
+#endif
+#ifndef ENDSESSION_CRITICAL
+#define ENDSESSION_CRITICAL 0x40000000
+#endif
static const QWinMessageMapping<uint> values[] = {
{ENDSESSION_CLOSEAPP, "Close application"},
{ENDSESSION_CRITICAL, "Force application end"},
@@ -881,12 +887,6 @@ QString decodeMSG(const MSG& msg)
parameters += QLatin1Char(')');
}
break;
-#ifndef ENDSESSION_CLOSEAPP
-#define ENDSESSION_CLOSEAPP 0x00000001
-#endif
-#ifndef ENDSESSION_CRITICAL
-#define ENDSESSION_CRITICAL 0x40000000
-#endif
case WM_QUERYENDSESSION:
parameters = QLatin1String("End session: ");
if (const char *logoffOption = sessionMgrLogOffOption(uint(wParam)))
diff --git a/src/corelib/kernel/qcoreevent.cpp b/src/corelib/kernel/qcoreevent.cpp
index aabd32b4a8..4cfc749386 100644
--- a/src/corelib/kernel/qcoreevent.cpp
+++ b/src/corelib/kernel/qcoreevent.cpp
@@ -424,7 +424,7 @@ struct QBasicAtomicBitField {
bool allocateSpecific(int which) noexcept
{
QBasicAtomicInteger<uint> &entry = data[which / BitsPerInt];
- const uint old = entry.load();
+ const uint old = entry.loadRelaxed();
const uint bit = 1U << (which % BitsPerInt);
return !(old & bit) // wasn't taken
&& entry.testAndSetRelaxed(old, old | bit); // still wasn't taken
@@ -445,10 +445,10 @@ struct QBasicAtomicBitField {
// Then again, this should never execute many iterations, so
// leave like this for now:
- for (uint i = next.load(); i < NumBits; ++i) {
+ for (uint i = next.loadRelaxed(); i < NumBits; ++i) {
if (allocateSpecific(i)) {
// remember next (possibly) free id:
- const uint oldNext = next.load();
+ const uint oldNext = next.loadRelaxed();
next.testAndSetRelaxed(oldNext, qMax(i + 1, oldNext));
return i;
}
diff --git a/src/corelib/kernel/qcoreglobaldata.cpp b/src/corelib/kernel/qcoreglobaldata.cpp
index 104be1b87b..7ff222f170 100644
--- a/src/corelib/kernel/qcoreglobaldata.cpp
+++ b/src/corelib/kernel/qcoreglobaldata.cpp
@@ -48,7 +48,7 @@ Q_GLOBAL_STATIC(QCoreGlobalData, globalInstance)
QCoreGlobalData::QCoreGlobalData()
#if QT_CONFIG(textcodec)
- : codecForLocale(0)
+ : codecForLocale(nullptr)
#endif
{
}
@@ -56,7 +56,7 @@ QCoreGlobalData::QCoreGlobalData()
QCoreGlobalData::~QCoreGlobalData()
{
#if QT_CONFIG(textcodec)
- codecForLocale = 0;
+ codecForLocale = nullptr;
QList<QTextCodec *> tmp = allCodecs;
allCodecs.clear();
codecCache.clear();
diff --git a/src/corelib/kernel/qelapsedtimer.cpp b/src/corelib/kernel/qelapsedtimer.cpp
index adb554b624..57825583dd 100644
--- a/src/corelib/kernel/qelapsedtimer.cpp
+++ b/src/corelib/kernel/qelapsedtimer.cpp
@@ -65,7 +65,7 @@ QT_BEGIN_NAMESPACE
\snippet qelapsedtimer/main.cpp 0
In this example, the timer is started by a call to start() and the
- elapsed timer is calculated by the elapsed() function.
+ elapsed time is calculated by the elapsed() function.
The time elapsed can also be used to recalculate the time available for
another operation, after the first one is complete. This is useful when
diff --git a/src/corelib/kernel/qeventdispatcher_glib.cpp b/src/corelib/kernel/qeventdispatcher_glib.cpp
index 34c2dde6a8..d9746ef6e2 100644
--- a/src/corelib/kernel/qeventdispatcher_glib.cpp
+++ b/src/corelib/kernel/qeventdispatcher_glib.cpp
@@ -261,7 +261,7 @@ static gboolean postEventSourcePrepare(GSource *s, gint *timeout)
*timeout = canWait ? -1 : 0;
GPostEventSource *source = reinterpret_cast<GPostEventSource *>(s);
- source->d->wakeUpCalled = source->serialNumber.load() != source->lastSerialNumber;
+ source->d->wakeUpCalled = source->serialNumber.loadRelaxed() != source->lastSerialNumber;
return !canWait || source->d->wakeUpCalled;
}
@@ -273,7 +273,7 @@ static gboolean postEventSourceCheck(GSource *source)
static gboolean postEventSourceDispatch(GSource *s, GSourceFunc, gpointer)
{
GPostEventSource *source = reinterpret_cast<GPostEventSource *>(s);
- source->lastSerialNumber = source->serialNumber.load();
+ source->lastSerialNumber = source->serialNumber.loadRelaxed();
QCoreApplication::sendPostedEvents();
source->d->runTimersOnceWithNormalPriority();
return true; // i dunno, george...
@@ -320,7 +320,7 @@ QEventDispatcherGlibPrivate::QEventDispatcherGlibPrivate(GMainContext *context)
// setup post event source
postEventSource = reinterpret_cast<GPostEventSource *>(g_source_new(&postEventSourceFuncs,
sizeof(GPostEventSource)));
- postEventSource->serialNumber.store(1);
+ postEventSource->serialNumber.storeRelaxed(1);
postEventSource->d = this;
g_source_set_can_recurse(&postEventSource->source, true);
g_source_attach(&postEventSource->source, mainContext);
diff --git a/src/corelib/kernel/qeventdispatcher_unix.cpp b/src/corelib/kernel/qeventdispatcher_unix.cpp
index df0cac0239..5bc65b7110 100644
--- a/src/corelib/kernel/qeventdispatcher_unix.cpp
+++ b/src/corelib/kernel/qeventdispatcher_unix.cpp
@@ -459,7 +459,7 @@ void QEventDispatcherUNIX::unregisterSocketNotifier(QSocketNotifier *notifier)
bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags)
{
Q_D(QEventDispatcherUNIX);
- d->interrupt.store(0);
+ d->interrupt.storeRelaxed(0);
// we are awake, broadcast it
emit awake();
@@ -470,13 +470,13 @@ bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags)
const bool wait_for_events = flags & QEventLoop::WaitForMoreEvents;
const bool canWait = (d->threadData->canWaitLocked()
- && !d->interrupt.load()
+ && !d->interrupt.loadRelaxed()
&& wait_for_events);
if (canWait)
emit aboutToBlock();
- if (d->interrupt.load())
+ if (d->interrupt.loadRelaxed())
return false;
timespec *tm = nullptr;
@@ -545,7 +545,7 @@ void QEventDispatcherUNIX::wakeUp()
void QEventDispatcherUNIX::interrupt()
{
Q_D(QEventDispatcherUNIX);
- d->interrupt.store(1);
+ d->interrupt.storeRelaxed(1);
wakeUp();
}
diff --git a/src/corelib/kernel/qeventdispatcher_win.cpp b/src/corelib/kernel/qeventdispatcher_win.cpp
index e0641a0282..c2e57a7924 100644
--- a/src/corelib/kernel/qeventdispatcher_win.cpp
+++ b/src/corelib/kernel/qeventdispatcher_win.cpp
@@ -247,7 +247,7 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA
Q_ASSERT(d != 0);
// Allow posting WM_QT_SENDPOSTEDEVENTS message.
- d->wakeUps.store(0);
+ d->wakeUps.storeRelaxed(0);
// We send posted events manually, if the window procedure was invoked
// by the foreign event loop (e.g. from the native modal dialog).
@@ -526,7 +526,7 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
wakeUp(); // trigger a call to sendPostedEvents()
}
- d->interrupt.store(false);
+ d->interrupt.storeRelaxed(false);
emit awake();
// To prevent livelocks, send posted events once per iteration.
@@ -545,7 +545,7 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
pHandles = &d->winEventNotifierActivatedEvent;
}
QVarLengthArray<MSG> processedTimers;
- while (!d->interrupt.load()) {
+ while (!d->interrupt.loadRelaxed()) {
MSG msg;
bool haveMessage;
@@ -590,7 +590,7 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) {
// Set result to 'true', if the message was sent by wakeUp().
if (msg.wParam == WMWP_QT_FROMWAKEUP) {
- d->wakeUps.store(0);
+ d->wakeUps.storeRelaxed(0);
retVal = true;
}
needWM_QT_SENDPOSTEDEVENTS = true;
@@ -639,7 +639,7 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
// still nothing - wait for message or signalled objects
canWait = (!retVal
- && !d->interrupt.load()
+ && !d->interrupt.loadRelaxed()
&& (flags & QEventLoop::WaitForMoreEvents));
if (canWait) {
emit aboutToBlock();
@@ -949,7 +949,7 @@ void QEventDispatcherWin32::activateEventNotifiers()
for (int i = 0; i < d->winEventNotifierList.count(); ++i) {
QWinEventNotifier *notifier = d->winEventNotifierList.at(i);
QWinEventNotifierPrivate *nd = QWinEventNotifierPrivate::get(notifier);
- if (nd->signaledCount.load() != 0) {
+ if (nd->signaledCount.loadRelaxed() != 0) {
--nd->signaledCount;
nd->unregisterWaitObject();
d->activateEventNotifier(notifier);
@@ -1014,7 +1014,7 @@ void QEventDispatcherWin32::wakeUp()
void QEventDispatcherWin32::interrupt()
{
Q_D(QEventDispatcherWin32);
- d->interrupt.store(true);
+ d->interrupt.storeRelaxed(true);
wakeUp();
}
diff --git a/src/corelib/kernel/qeventloop.cpp b/src/corelib/kernel/qeventloop.cpp
index a6cc51621a..2104b22095 100644
--- a/src/corelib/kernel/qeventloop.cpp
+++ b/src/corelib/kernel/qeventloop.cpp
@@ -135,7 +135,7 @@ bool QEventLoop::processEvents(ProcessEventsFlags flags)
Q_D(QEventLoop);
if (!d->threadData->hasEventDispatcher())
return false;
- return d->threadData->eventDispatcher.load()->processEvents(flags);
+ return d->threadData->eventDispatcher.loadRelaxed()->processEvents(flags);
}
/*!
@@ -225,7 +225,7 @@ int QEventLoop::exec(ProcessEventsFlags flags)
processEvents(flags | WaitForMoreEvents | EventLoopExec);
ref.exceptionCaught = false;
- return d->returnCode.load();
+ return d->returnCode.loadRelaxed();
}
/*!
@@ -279,9 +279,9 @@ void QEventLoop::exit(int returnCode)
if (!d->threadData->hasEventDispatcher())
return;
- d->returnCode.store(returnCode);
+ d->returnCode.storeRelaxed(returnCode);
d->exit.storeRelease(true);
- d->threadData->eventDispatcher.load()->interrupt();
+ d->threadData->eventDispatcher.loadRelaxed()->interrupt();
#ifdef Q_OS_WASM
// QEventLoop::exec() never returns in emscripten. We implement approximate behavior here.
@@ -318,7 +318,7 @@ void QEventLoop::wakeUp()
Q_D(QEventLoop);
if (!d->threadData->hasEventDispatcher())
return;
- d->threadData->eventDispatcher.load()->wakeUp();
+ d->threadData->eventDispatcher.loadRelaxed()->wakeUp();
}
diff --git a/src/corelib/kernel/qeventloop_p.h b/src/corelib/kernel/qeventloop_p.h
index dcbb5c63c6..4ad6d92007 100644
--- a/src/corelib/kernel/qeventloop_p.h
+++ b/src/corelib/kernel/qeventloop_p.h
@@ -63,8 +63,8 @@ public:
inline QEventLoopPrivate()
: inExec(false)
{
- returnCode.store(-1);
- exit.store(true);
+ returnCode.storeRelaxed(-1);
+ exit.storeRelaxed(true);
}
QAtomicInt quitLockRef;
diff --git a/src/corelib/kernel/qjni.cpp b/src/corelib/kernel/qjni.cpp
index 75a2436d9d..5f652d70e3 100644
--- a/src/corelib/kernel/qjni.cpp
+++ b/src/corelib/kernel/qjni.cpp
@@ -47,9 +47,9 @@
QT_BEGIN_NAMESPACE
-static inline QString keyBase()
+static inline QLatin1String keyBase()
{
- return QStringLiteral("%1%2:%3");
+ return QLatin1String("%1%2:%3");
}
static QString qt_convertJString(jstring string)
@@ -154,7 +154,7 @@ static jmethodID getCachedMethodID(JNIEnv *env,
if (className.isEmpty())
return getMethodID(env, clazz, name, sig, isStatic);
- const QString key = keyBase().arg(QLatin1String(className)).arg(QLatin1String(name)).arg(QLatin1String(sig));
+ const QString key = keyBase().arg(QLatin1String(className), QLatin1String(name), QLatin1String(sig));
QHash<QString, jmethodID>::const_iterator it;
{
@@ -206,7 +206,7 @@ static jfieldID getCachedFieldID(JNIEnv *env,
if (className.isNull())
return getFieldID(env, clazz, name, sig, isStatic);
- const QString key = keyBase().arg(QLatin1String(className)).arg(QLatin1String(name)).arg(QLatin1String(sig));
+ const QString key = keyBase().arg(QLatin1String(className), QLatin1String(name), QLatin1String(sig));
QHash<QString, jfieldID>::const_iterator it;
{
diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp
index 855d36794d..754f5a13e4 100644
--- a/src/corelib/kernel/qmetatype.cpp
+++ b/src/corelib/kernel/qmetatype.cpp
@@ -517,12 +517,12 @@ static const struct { const char * typeName; int typeNameLength; int type; } typ
QT_FOR_EACH_STATIC_TYPE(QT_ADD_STATIC_METATYPE)
QT_FOR_EACH_STATIC_ALIAS_TYPE(QT_ADD_STATIC_METATYPE_ALIASES_ITER)
QT_FOR_EACH_STATIC_HACKS_TYPE(QT_ADD_STATIC_METATYPE_HACKS_ITER)
- {0, 0, QMetaType::UnknownType}
+ {nullptr, 0, QMetaType::UnknownType}
};
-Q_CORE_EXPORT const QMetaTypeInterface *qMetaTypeGuiHelper = 0;
-Q_CORE_EXPORT const QMetaTypeInterface *qMetaTypeWidgetsHelper = 0;
-Q_CORE_EXPORT const QMetaObject *qMetaObjectWidgetsHelper = 0;
+Q_CORE_EXPORT const QMetaTypeInterface *qMetaTypeGuiHelper = nullptr;
+Q_CORE_EXPORT const QMetaTypeInterface *qMetaTypeWidgetsHelper = nullptr;
+Q_CORE_EXPORT const QMetaObject *qMetaObjectWidgetsHelper = nullptr;
class QCustomTypeInfo : public QMetaTypeInterface
{
@@ -557,7 +557,7 @@ public:
{
const QWriteLocker locker(&lock);
const T* &fun = map[k];
- if (fun != 0)
+ if (fun)
return false;
fun = f;
return true;
@@ -566,7 +566,7 @@ public:
const T *function(Key k) const
{
const QReadLocker locker(&lock);
- return map.value(k, 0);
+ return map.value(k, nullptr);
}
void remove(int from, int to)
@@ -587,13 +587,7 @@ QMetaTypeComparatorRegistry;
typedef QMetaTypeFunctionRegistry<QtPrivate::AbstractDebugStreamFunction,int>
QMetaTypeDebugStreamRegistry;
-namespace
-{
-union CheckThatItIsPod
-{ // This should break if QMetaTypeInterface is not a POD type
- QMetaTypeInterface iface;
-};
-}
+Q_STATIC_ASSERT(std::is_pod<QMetaTypeInterface>::value);
Q_DECLARE_TYPEINFO(QCustomTypeInfo, Q_MOVABLE_TYPE);
Q_GLOBAL_STATIC(QVector<QCustomTypeInfo>, customTypes)
@@ -973,7 +967,7 @@ static inline int qMetaTypeStaticType(const char *typeName, int length)
The extra \a firstInvalidIndex parameter is an easy way to avoid
iterating over customTypes() a second time in registerNormalizedType().
*/
-static int qMetaTypeCustomType_unlocked(const char *typeName, int length, int *firstInvalidIndex = 0)
+static int qMetaTypeCustomType_unlocked(const char *typeName, int length, int *firstInvalidIndex = nullptr)
{
const QVector<QCustomTypeInfo> * const ct = customTypes();
if (!ct)
@@ -1006,7 +1000,7 @@ int QMetaType::registerType(const char *typeName, Deleter deleter,
{
return registerType(typeName, deleter, creator,
QtMetaTypePrivate::QMetaTypeFunctionHelper<void>::Destruct,
- QtMetaTypePrivate::QMetaTypeFunctionHelper<void>::Construct, 0, TypeFlags(), 0);
+ QtMetaTypePrivate::QMetaTypeFunctionHelper<void>::Construct, 0, TypeFlags(), nullptr);
}
/*!
@@ -1613,7 +1607,7 @@ void *QMetaType::create(int type, const void *copy)
QMetaType info(type);
if (int size = info.sizeOf())
return info.construct(operator new(size), copy);
- return 0;
+ return nullptr;
}
/*!
@@ -1639,14 +1633,18 @@ class TypeConstructor {
static void *Construct(const int type, void *where, const void *copy)
{
if (QModulesPrivate::QTypeModuleInfo<T>::IsGui)
- return Q_LIKELY(qMetaTypeGuiHelper) ? qMetaTypeGuiHelper[type - QMetaType::FirstGuiType].constructor(where, copy) : 0;
+ return Q_LIKELY(qMetaTypeGuiHelper)
+ ? qMetaTypeGuiHelper[type - QMetaType::FirstGuiType].constructor(where, copy)
+ : nullptr;
if (QModulesPrivate::QTypeModuleInfo<T>::IsWidget)
- return Q_LIKELY(qMetaTypeWidgetsHelper) ? qMetaTypeWidgetsHelper[type - QMetaType::FirstWidgetsType].constructor(where, copy) : 0;
+ return Q_LIKELY(qMetaTypeWidgetsHelper)
+ ? qMetaTypeWidgetsHelper[type - QMetaType::FirstWidgetsType].constructor(where, copy)
+ : nullptr;
// This point can be reached only for known types that definition is not available, for example
// in bootstrap mode. We have no other choice then ignore it.
- return 0;
+ return nullptr;
}
};
public:
@@ -1670,7 +1668,7 @@ private:
{
QReadLocker locker(customTypesLock());
if (Q_UNLIKELY(type < QMetaType::User || !ct || ct->count() <= type - QMetaType::User))
- return 0;
+ return nullptr;
const auto &typeInfo = ct->at(type - QMetaType::User);
ctor = typeInfo.constructor;
tctor = typeInfo.typedConstructor;
@@ -1715,7 +1713,7 @@ private:
void *QMetaType::construct(int type, void *where, const void *copy)
{
if (!where)
- return 0;
+ return nullptr;
TypeConstructor constructor(type, where);
return QMetaTypeSwitcher::switcher<void*>(constructor, type, copy);
}
@@ -1861,7 +1859,7 @@ private:
int QMetaType::sizeOf(int type)
{
SizeOf sizeOf(type);
- return QMetaTypeSwitcher::switcher<int>(sizeOf, type, 0);
+ return QMetaTypeSwitcher::switcher<int>(sizeOf, type);
}
namespace {
@@ -1925,7 +1923,7 @@ private:
QMetaType::TypeFlags QMetaType::typeFlags(int type)
{
Flags flags(type);
- return static_cast<QMetaType::TypeFlags>(QMetaTypeSwitcher::switcher<quint32>(flags, type, 0));
+ return static_cast<QMetaType::TypeFlags>(QMetaTypeSwitcher::switcher<quint32>(flags, type));
}
#ifndef QT_BOOTSTRAPPED
@@ -1948,17 +1946,21 @@ public:
{
static const QMetaObject *MetaObject(int type) {
if (QModulesPrivate::QTypeModuleInfo<T>::IsGui)
- return Q_LIKELY(qMetaTypeGuiHelper) ? qMetaTypeGuiHelper[type - QMetaType::FirstGuiType].metaObject : 0;
+ return Q_LIKELY(qMetaTypeGuiHelper)
+ ? qMetaTypeGuiHelper[type - QMetaType::FirstGuiType].metaObject
+ : nullptr;
if (QModulesPrivate::QTypeModuleInfo<T>::IsWidget)
- return Q_LIKELY(qMetaTypeWidgetsHelper) ? qMetaTypeWidgetsHelper[type - QMetaType::FirstWidgetsType].metaObject : 0;
+ return Q_LIKELY(qMetaTypeWidgetsHelper)
+ ? qMetaTypeWidgetsHelper[type - QMetaType::FirstWidgetsType].metaObject
+ : nullptr;
return 0;
}
};
template <typename T>
const QMetaObject *delegate(const T *) { return MetaObjectImpl<T>::MetaObject(m_type); }
- const QMetaObject *delegate(const void*) { return 0; }
- const QMetaObject *delegate(const QMetaTypeSwitcher::UnknownType*) { return 0; }
+ const QMetaObject *delegate(const void*) { return nullptr; }
+ const QMetaObject *delegate(const QMetaTypeSwitcher::UnknownType*) { return nullptr; }
const QMetaObject *delegate(const QMetaTypeSwitcher::NotBuiltinType*) { return customMetaObject(m_type); }
private:
const int m_type;
@@ -1966,10 +1968,10 @@ private:
{
const QVector<QCustomTypeInfo> * const ct = customTypes();
if (Q_UNLIKELY(!ct || type < QMetaType::User))
- return 0;
+ return nullptr;
QReadLocker locker(customTypesLock());
if (Q_UNLIKELY(ct->count() <= type - QMetaType::User))
- return 0;
+ return nullptr;
return ct->at(type - QMetaType::User).metaObject;
}
};
@@ -1987,10 +1989,10 @@ const QMetaObject *QMetaType::metaObjectForType(int type)
{
#ifndef QT_BOOTSTRAPPED
MetaObject mo(type);
- return QMetaTypeSwitcher::switcher<const QMetaObject*>(mo, type, 0);
+ return QMetaTypeSwitcher::switcher<const QMetaObject*>(mo, type);
#else
Q_UNUSED(type);
- return 0;
+ return nullptr;
#endif
}
@@ -2187,7 +2189,7 @@ private:
QMetaType QMetaType::typeInfo(const int type)
{
TypeInfo typeInfo(type);
- QMetaTypeSwitcher::switcher<void>(typeInfo, type, 0);
+ QMetaTypeSwitcher::switcher<void>(typeInfo, type);
return (typeInfo.info.constructor || typeInfo.info.typedConstructor)
? QMetaType(static_cast<ExtensionFlag>(QMetaType::CreateEx | QMetaType::DestroyEx |
(typeInfo.info.typedConstructor ? QMetaType::ConstructEx | QMetaType::DestructEx : 0))
@@ -2307,7 +2309,7 @@ void QMetaType::dtor()
void *QMetaType::createExtended(const void *copy) const
{
if (m_typeId == QMetaType::UnknownType)
- return 0;
+ return nullptr;
if (Q_UNLIKELY(m_typedConstructor && !m_constructor))
return m_typedConstructor(m_typeId, operator new(m_size), copy);
return m_constructor(operator new(m_size), copy);
@@ -2387,7 +2389,7 @@ uint QMetaType::sizeExtended() const
*/
QMetaType::TypeFlags QMetaType::flagsExtended() const
{
- return 0;
+ return { };
}
/*!
@@ -2400,7 +2402,7 @@ QMetaType::TypeFlags QMetaType::flagsExtended() const
*/
const QMetaObject *QMetaType::metaObjectExtended() const
{
- return 0;
+ return nullptr;
}
@@ -2409,7 +2411,7 @@ namespace QtPrivate
const QMetaObject *metaObjectForQWidget()
{
if (!qMetaTypeWidgetsHelper)
- return 0;
+ return nullptr;
return qMetaObjectWidgetsHelper;
}
}
diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h
index 12d4bc97aa..a97764ede4 100644
--- a/src/corelib/kernel/qmetatype.h
+++ b/src/corelib/kernel/qmetatype.h
@@ -1715,12 +1715,17 @@ namespace QtPrivate {
}
};
+ // hack to delay name lookup to instantiation time by making
+ // EnableInternalData a dependent name:
+ template <typename T>
+ struct EnableInternalDataWrap;
+
template<typename T>
struct QSmartPointerConvertFunctor<QWeakPointer<T> >
{
QObject* operator()(const QWeakPointer<T> &p) const
{
- return p.internalData();
+ return QtPrivate::EnableInternalDataWrap<T>::internalData(p);
}
};
}
@@ -1994,7 +1999,7 @@ struct QMetaTypeId< SINGLE_ARG_TEMPLATE<T> > \
static int qt_metatype_id() \
{ \
static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0); \
- if (const int id = metatype_id.load()) \
+ if (const int id = metatype_id.loadRelaxed()) \
return id; \
const char *tName = QMetaType::typeName(qMetaTypeId<T>()); \
Q_ASSERT(tName); \
diff --git a/src/corelib/kernel/qmetatype_p.h b/src/corelib/kernel/qmetatype_p.h
index 0846193e66..97d6001937 100644
--- a/src/corelib/kernel/qmetatype_p.h
+++ b/src/corelib/kernel/qmetatype_p.h
@@ -140,12 +140,12 @@ public:
/*saveOp*/(QtMetaTypePrivate::QMetaTypeFunctionHelper<Type, QtMetaTypePrivate::TypeDefinition<Type>::IsAvailable>::Save), \
/*loadOp*/(QtMetaTypePrivate::QMetaTypeFunctionHelper<Type, QtMetaTypePrivate::TypeDefinition<Type>::IsAvailable>::Load),
# define QT_METATYPE_INTERFACE_INIT_EMPTY_DATASTREAM_IMPL(Type) \
- /*saveOp*/ 0, \
- /*loadOp*/ 0,
+ /*saveOp*/ nullptr, \
+ /*loadOp*/ nullptr,
#else
# define QT_METATYPE_INTERFACE_INIT_EMPTY_DATASTREAM_IMPL(Type) \
- /*saveOp*/ 0, \
- /*loadOp*/ 0,
+ /*saveOp*/ nullptr, \
+ /*loadOp*/ nullptr,
# define QT_METATYPE_INTERFACE_INIT_DATASTREAM_IMPL(Type) \
QT_METATYPE_INTERFACE_INIT_EMPTY_DATASTREAM_IMPL(Type)
#endif
@@ -153,7 +153,7 @@ public:
#ifndef QT_BOOTSTRAPPED
#define METAOBJECT_DELEGATE(Type) (QtPrivate::MetaObjectForType<Type>::value())
#else
-#define METAOBJECT_DELEGATE(Type) 0
+#define METAOBJECT_DELEGATE(Type) nullptr
#endif
#define QT_METATYPE_INTERFACE_INIT_IMPL(Type, DATASTREAM_DELEGATE) \
@@ -184,11 +184,11 @@ public:
#define QT_METATYPE_INTERFACE_INIT_EMPTY() \
{ \
QT_METATYPE_INTERFACE_INIT_EMPTY_DATASTREAM_IMPL(void) \
- /*constructor*/ 0, \
- /*destructor*/ 0, \
+ /*constructor*/ nullptr, \
+ /*destructor*/ nullptr, \
/*size*/ 0, \
/*flags*/ 0, \
- /*metaObject*/ 0 , \
+ /*metaObject*/ nullptr , \
/*typedConstructor*/ nullptr, \
/*typedDestructor*/ nullptr \
}
diff --git a/src/corelib/kernel/qmetatypeswitcher_p.h b/src/corelib/kernel/qmetatypeswitcher_p.h
index 154fb8ab7f..dabc70f4b0 100644
--- a/src/corelib/kernel/qmetatypeswitcher_p.h
+++ b/src/corelib/kernel/qmetatypeswitcher_p.h
@@ -60,7 +60,7 @@ public:
class NotBuiltinType; // type is not a built-in type, but it may be a custom type or an unknown type
class UnknownType; // type not known to QMetaType system
template<class ReturnType, class DelegateObject>
- static ReturnType switcher(DelegateObject &logic, int type, const void *data);
+ static ReturnType switcher(DelegateObject &logic, int type, const void *data = nullptr);
};
diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp
index 965857d408..8f80be30bd 100644
--- a/src/corelib/kernel/qobject.cpp
+++ b/src/corelib/kernel/qobject.cpp
@@ -58,6 +58,7 @@
#include <qdebug.h>
#include <qpair.h>
#include <qvarlengtharray.h>
+#include <qscopeguard.h>
#include <qset.h>
#if QT_CONFIG(thread)
#include <qsemaphore.h>
@@ -81,7 +82,7 @@ Q_CORE_EXPORT QBasicAtomicPointer<QSignalSpyCallbackSet> qt_signal_spy_callback_
void qt_register_signal_spy_callbacks(QSignalSpyCallbackSet *callback_set)
{
- qt_signal_spy_callback_set.store(callback_set);
+ qt_signal_spy_callback_set.storeRelease(callback_set);
}
QDynamicMetaObjectData::~QDynamicMetaObjectData()
@@ -221,7 +222,7 @@ QObjectPrivate::~QObjectPrivate()
if (Q_LIKELY(threadData->thread == QThread::currentThread())) {
// unregister pending timers
if (threadData->hasEventDispatcher())
- threadData->eventDispatcher.load()->unregisterTimers(q_ptr);
+ threadData->eventDispatcher.loadRelaxed()->unregisterTimers(q_ptr);
// release the timer ids back to the pool
for (int i = 0; i < extraData->runningTimers.size(); ++i)
@@ -267,17 +268,17 @@ bool QObjectPrivate::isSender(const QObject *receiver, const char *signal) const
{
Q_Q(const QObject);
int signal_index = signalIndex(signal);
- ConnectionData *cd = connections.load();
+ ConnectionData *cd = connections.loadRelaxed();
if (signal_index < 0 || !cd)
return false;
QBasicMutexLocker locker(signalSlotLock(q));
if (signal_index < cd->signalVectorCount()) {
- const QObjectPrivate::Connection *c = cd->signalVector.load()->at(signal_index).first.load();
+ const QObjectPrivate::Connection *c = cd->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed();
while (c) {
- if (c->receiver.load() == receiver)
+ if (c->receiver.loadRelaxed() == receiver)
return true;
- c = c->nextConnectionList.load();
+ c = c->nextConnectionList.loadRelaxed();
}
}
return false;
@@ -288,17 +289,17 @@ QObjectList QObjectPrivate::receiverList(const char *signal) const
{
QObjectList returnValue;
int signal_index = signalIndex(signal);
- ConnectionData *cd = connections.load();
+ ConnectionData *cd = connections.loadRelaxed();
if (signal_index < 0 || !cd)
return returnValue;
if (signal_index < cd->signalVectorCount()) {
- const QObjectPrivate::Connection *c = cd->signalVector.load()->at(signal_index).first.load();
+ const QObjectPrivate::Connection *c = cd->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed();
while (c) {
- QObject *r = c->receiver.load();
+ QObject *r = c->receiver.loadRelaxed();
if (r)
returnValue << r;
- c = c->nextConnectionList.load();
+ c = c->nextConnectionList.loadRelaxed();
}
}
return returnValue;
@@ -308,7 +309,7 @@ QObjectList QObjectPrivate::receiverList(const char *signal) const
QObjectList QObjectPrivate::senderList() const
{
QObjectList returnValue;
- ConnectionData *cd = connections.load();
+ ConnectionData *cd = connections.loadRelaxed();
if (cd) {
QBasicMutexLocker locker(signalSlotLock(q_func()));
for (Connection *c = cd->senders; c; c = c->next)
@@ -331,24 +332,24 @@ void QObjectPrivate::addConnection(int signal, Connection *c)
{
Q_ASSERT(c->sender == q_ptr);
ensureConnectionData();
- ConnectionData *cd = connections.load();
+ ConnectionData *cd = connections.loadRelaxed();
cd->resizeSignalVector(signal + 1);
ConnectionList &connectionList = cd->connectionsForSignal(signal);
- if (connectionList.last.load()) {
- Q_ASSERT(connectionList.last.load()->receiver.load());
- connectionList.last.load()->nextConnectionList.store(c);
+ if (connectionList.last.loadRelaxed()) {
+ Q_ASSERT(connectionList.last.loadRelaxed()->receiver.loadRelaxed());
+ connectionList.last.loadRelaxed()->nextConnectionList.storeRelaxed(c);
} else {
- connectionList.first.store(c);
+ connectionList.first.storeRelaxed(c);
}
c->id = ++cd->currentConnectionId;
- c->prevConnectionList = connectionList.last.load();
- connectionList.last.store(c);
+ c->prevConnectionList = connectionList.last.loadRelaxed();
+ connectionList.last.storeRelaxed(c);
- QObjectPrivate *rd = QObjectPrivate::get(c->receiver.load());
+ QObjectPrivate *rd = QObjectPrivate::get(c->receiver.loadRelaxed());
rd->ensureConnectionData();
- c->prev = &(rd->connections.load()->senders);
+ c->prev = &(rd->connections.loadRelaxed()->senders);
c->next = *c->prev;
*c->prev = c;
if (c->next)
@@ -357,17 +358,17 @@ void QObjectPrivate::addConnection(int signal, Connection *c)
void QObjectPrivate::ConnectionData::removeConnection(QObjectPrivate::Connection *c)
{
- Q_ASSERT(c->receiver.load());
- ConnectionList &connections = signalVector.load()->at(c->signal_index);
- c->receiver.store(nullptr);
- QThreadData *td = c->receiverThreadData.load();
+ Q_ASSERT(c->receiver.loadRelaxed());
+ ConnectionList &connections = signalVector.loadRelaxed()->at(c->signal_index);
+ c->receiver.storeRelaxed(nullptr);
+ QThreadData *td = c->receiverThreadData.loadRelaxed();
if (td)
td->deref();
- c->receiverThreadData.store(nullptr);
+ c->receiverThreadData.storeRelaxed(nullptr);
#ifndef QT_NO_DEBUG
bool found = false;
- for (Connection *cc = connections.first.load(); cc; cc = cc->nextConnectionList.load()) {
+ for (Connection *cc = connections.first.loadRelaxed(); cc; cc = cc->nextConnectionList.loadRelaxed()) {
if (cc == c) {
found = true;
break;
@@ -382,29 +383,29 @@ void QObjectPrivate::ConnectionData::removeConnection(QObjectPrivate::Connection
c->next->prev = c->prev;
c->prev = nullptr;
- if (connections.first.load() == c)
- connections.first.store(c->nextConnectionList.load());
- if (connections.last.load() == c)
- connections.last.store(c->prevConnectionList);
- Q_ASSERT(signalVector.load()->at(c->signal_index).first.load() != c);
- Q_ASSERT(signalVector.load()->at(c->signal_index).last.load() != c);
+ if (connections.first.loadRelaxed() == c)
+ connections.first.storeRelaxed(c->nextConnectionList.loadRelaxed());
+ if (connections.last.loadRelaxed() == c)
+ connections.last.storeRelaxed(c->prevConnectionList);
+ Q_ASSERT(signalVector.loadRelaxed()->at(c->signal_index).first.loadRelaxed() != c);
+ Q_ASSERT(signalVector.loadRelaxed()->at(c->signal_index).last.loadRelaxed() != c);
// keep c->nextConnectionList intact, as it might still get accessed by activate
- Connection *n = c->nextConnectionList.load();
+ Connection *n = c->nextConnectionList.loadRelaxed();
if (n)
n->prevConnectionList = c->prevConnectionList;
if (c->prevConnectionList)
- c->prevConnectionList->nextConnectionList.store(n);
+ c->prevConnectionList->nextConnectionList.storeRelaxed(n);
c->prevConnectionList = nullptr;
- Q_ASSERT(c != orphaned.load());
+ Q_ASSERT(c != orphaned.loadRelaxed());
// add c to orphanedConnections
- c->nextInOrphanList = orphaned.load();
- orphaned.store(c);
+ c->nextInOrphanList = orphaned.loadRelaxed();
+ orphaned.storeRelaxed(c);
#ifndef QT_NO_DEBUG
found = false;
- for (Connection *cc = connections.first.load(); cc; cc = cc->nextConnectionList.load()) {
+ for (Connection *cc = connections.first.loadRelaxed(); cc; cc = cc->nextConnectionList.loadRelaxed()) {
if (cc == c) {
found = true;
break;
@@ -426,8 +427,8 @@ void QObjectPrivate::ConnectionData::cleanOrphanedConnectionsImpl(QObject *sende
// Since ref == 1, no activate() is in process since we locked the mutex. That implies,
// that nothing can reference the orphaned connection objects anymore and they can
// be safely deleted
- c = orphaned.load();
- orphaned.store(nullptr);
+ c = orphaned.loadRelaxed();
+ orphaned.storeRelaxed(nullptr);
}
deleteOrphaned(c);
}
@@ -442,7 +443,7 @@ void QObjectPrivate::ConnectionData::deleteOrphaned(QObjectPrivate::ConnectionOr
} else {
QObjectPrivate::Connection *c = static_cast<Connection *>(o);
next = c->nextInOrphanList;
- Q_ASSERT(!c->receiver.load());
+ Q_ASSERT(!c->receiver.loadRelaxed());
Q_ASSERT(!c->prev);
c->freeSlotObject();
c->deref();
@@ -462,22 +463,22 @@ bool QObjectPrivate::isSignalConnected(uint signalIndex, bool checkDeclarative)
if (checkDeclarative && isDeclarativeSignalConnected(signalIndex))
return true;
- ConnectionData *cd = connections.load();
+ ConnectionData *cd = connections.loadRelaxed();
if (!cd)
return false;
- SignalVector *signalVector = cd->signalVector.load();
+ SignalVector *signalVector = cd->signalVector.loadRelaxed();
if (!signalVector)
return false;
- if (signalVector->at(-1).first.load())
+ if (signalVector->at(-1).first.loadRelaxed())
return true;
if (signalIndex < uint(cd->signalVectorCount())) {
- const QObjectPrivate::Connection *c = signalVector->at(signalIndex).first.load();
+ const QObjectPrivate::Connection *c = signalVector->at(signalIndex).first.loadRelaxed();
while (c) {
- if (c->receiver.load())
+ if (c->receiver.loadRelaxed())
return true;
- c = c->nextConnectionList.load();
+ c = c->nextConnectionList.loadRelaxed();
}
}
return false;
@@ -485,10 +486,10 @@ bool QObjectPrivate::isSignalConnected(uint signalIndex, bool checkDeclarative)
bool QObjectPrivate::maybeSignalConnected(uint signalIndex) const
{
- ConnectionData *cd = connections.load();
+ ConnectionData *cd = connections.loadRelaxed();
if (!cd)
return false;
- SignalVector *signalVector = cd->signalVector.load();
+ SignalVector *signalVector = cd->signalVector.loadRelaxed();
if (!signalVector)
return false;
@@ -851,6 +852,8 @@ static bool check_parent_thread(QObject *parent,
QObject::QObject(QObject *parent)
: d_ptr(new QObjectPrivate)
{
+ Q_ASSERT_X(this != parent, Q_FUNC_INFO, "Cannot parent a QObject to itself");
+
Q_D(QObject);
d_ptr->q_ptr = this;
d->threadData = (parent && !parent->thread()) ? parent->d_func()->threadData : QThreadData::current();
@@ -879,6 +882,8 @@ QObject::QObject(QObject *parent)
QObject::QObject(QObjectPrivate &dd, QObject *parent)
: d_ptr(&dd)
{
+ Q_ASSERT_X(this != parent, Q_FUNC_INFO, "Cannot parent a QObject to itself");
+
Q_D(QObject);
d_ptr->q_ptr = this;
d->threadData = (parent && !parent->thread()) ? parent->d_func()->threadData : QThreadData::current();
@@ -939,15 +944,15 @@ QObject::~QObject()
d->wasDeleted = true;
d->blockSig = 0; // unblock signals so we always emit destroyed()
- QtSharedPointer::ExternalRefCountData *sharedRefcount = d->sharedRefcount.load();
+ QtSharedPointer::ExternalRefCountData *sharedRefcount = d->sharedRefcount.loadRelaxed();
if (sharedRefcount) {
- if (sharedRefcount->strongref.load() > 0) {
+ if (sharedRefcount->strongref.loadRelaxed() > 0) {
qWarning("QObject: shared QObject was deleted directly. The program is malformed and may crash.");
// but continue deleting, it's too late to stop anyway
}
// indicate to all QWeakPointers that this QObject has now been deleted
- sharedRefcount->strongref.store(0);
+ sharedRefcount->strongref.storeRelaxed(0);
if (!sharedRefcount->weakref.deref())
delete sharedRefcount;
}
@@ -966,7 +971,7 @@ QObject::~QObject()
}
}
- QObjectPrivate::ConnectionData *cd = d->connections.load();
+ QObjectPrivate::ConnectionData *cd = d->connections.loadRelaxed();
if (cd) {
if (cd->currentSender) {
cd->currentSender->receiverDeleted();
@@ -981,14 +986,14 @@ QObject::~QObject()
for (int signal = -1; signal < receiverCount; ++signal) {
QObjectPrivate::ConnectionList &connectionList = cd->connectionsForSignal(signal);
- while (QObjectPrivate::Connection *c = connectionList.first.load()) {
+ while (QObjectPrivate::Connection *c = connectionList.first.loadRelaxed()) {
Q_ASSERT(c->receiver);
- QBasicMutex *m = signalSlotLock(c->receiver.load());
+ QBasicMutex *m = signalSlotLock(c->receiver.loadRelaxed());
bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m);
if (c->receiver) {
cd->removeConnection(c);
- Q_ASSERT(connectionList.first.load() != c);
+ Q_ASSERT(connectionList.first.loadRelaxed() != c);
}
if (needToUnlock)
m->unlock();
@@ -1014,7 +1019,7 @@ QObject::~QObject()
continue;
}
- QObjectPrivate::ConnectionData *senderData = sender->d_func()->connections.load();
+ QObjectPrivate::ConnectionData *senderData = sender->d_func()->connections.loadRelaxed();
Q_ASSERT(senderData);
QtPrivate::QSlotObjectBase *slotObj = nullptr;
@@ -1036,11 +1041,11 @@ QObject::~QObject()
// invalidate all connections on the object and make sure
// activate() will skip them
- cd->currentConnectionId.store(0);
+ cd->currentConnectionId.storeRelaxed(0);
}
if (cd && !cd->ref.deref())
delete cd;
- d->connections.store(nullptr);
+ d->connections.storeRelaxed(nullptr);
if (!d->children.isEmpty())
d->deleteChildren();
@@ -1060,7 +1065,7 @@ QObject::~QObject()
QObjectPrivate::Connection::~Connection()
{
if (ownArgumentTypes) {
- const int *v = argumentTypes.load();
+ const int *v = argumentTypes.loadRelaxed();
if (v != &DIRECT_CONNECTION_ONLY)
delete [] v;
}
@@ -1269,7 +1274,7 @@ bool QObject::event(QEvent *e)
{
QAbstractMetaCallEvent *mce = static_cast<QAbstractMetaCallEvent*>(e);
- if (!d_func()->connections.load()) {
+ if (!d_func()->connections.loadRelaxed()) {
QBasicMutexLocker locker(signalSlotLock(this));
d_func()->ensureConnectionData();
}
@@ -1282,7 +1287,7 @@ bool QObject::event(QEvent *e)
case QEvent::ThreadChange: {
Q_D(QObject);
QThreadData *threadData = d->threadData;
- QAbstractEventDispatcher *eventDispatcher = threadData->eventDispatcher.load();
+ QAbstractEventDispatcher *eventDispatcher = threadData->eventDispatcher.loadRelaxed();
if (eventDispatcher) {
QList<QAbstractEventDispatcher::TimerInfo> timers = eventDispatcher->registeredTimers(this);
if (!timers.isEmpty()) {
@@ -1517,7 +1522,7 @@ void QObject::moveToThread(QThread *targetThread)
} else if (d->threadData != currentData) {
qWarning("QObject::moveToThread: Current thread (%p) is not the object's thread (%p).\n"
"Cannot move to target thread (%p)\n",
- currentData->thread.load(), d->threadData->thread.load(), targetData ? targetData->thread.load() : nullptr);
+ currentData->thread.loadRelaxed(), d->threadData->thread.loadRelaxed(), targetData ? targetData->thread.loadRelaxed() : nullptr);
#ifdef Q_OS_MAC
qWarning("You might be loading two sets of Qt binaries into the same process. "
@@ -1582,11 +1587,11 @@ void QObjectPrivate::setThreadData_helper(QThreadData *currentData, QThreadData
}
if (eventsMoved > 0 && targetData->hasEventDispatcher()) {
targetData->canWait = false;
- targetData->eventDispatcher.load()->wakeUp();
+ targetData->eventDispatcher.loadRelaxed()->wakeUp();
}
// the current emitting thread shouldn't restore currentSender after calling moveToThread()
- ConnectionData *cd = connections.load();
+ ConnectionData *cd = connections.loadRelaxed();
if (cd) {
if (cd->currentSender) {
cd->currentSender->receiverDeleted();
@@ -1597,14 +1602,14 @@ void QObjectPrivate::setThreadData_helper(QThreadData *currentData, QThreadData
if (cd) {
auto *c = cd->senders;
while (c) {
- QObject *r = c->receiver.load();
+ QObject *r = c->receiver.loadRelaxed();
if (r) {
Q_ASSERT(r == q);
targetData->ref();
- QThreadData *old = c->receiverThreadData.load();
+ QThreadData *old = c->receiverThreadData.loadRelaxed();
if (old)
old->deref();
- c->receiverThreadData.store(targetData);
+ c->receiverThreadData.storeRelaxed(targetData);
}
c = c->next;
}
@@ -1627,7 +1632,7 @@ void QObjectPrivate::_q_reregisterTimers(void *pointer)
{
Q_Q(QObject);
QList<QAbstractEventDispatcher::TimerInfo> *timerList = reinterpret_cast<QList<QAbstractEventDispatcher::TimerInfo> *>(pointer);
- QAbstractEventDispatcher *eventDispatcher = threadData->eventDispatcher.load();
+ QAbstractEventDispatcher *eventDispatcher = threadData->eventDispatcher.loadRelaxed();
for (int i = 0; i < timerList->size(); ++i) {
const QAbstractEventDispatcher::TimerInfo &ti = timerList->at(i);
eventDispatcher->registerTimer(ti.timerId, ti.interval, ti.timerType, q);
@@ -1693,7 +1698,7 @@ int QObject::startTimer(int interval, Qt::TimerType timerType)
qWarning("QObject::startTimer: Timers cannot be started from another thread");
return 0;
}
- int timerId = d->threadData->eventDispatcher.load()->registerTimer(interval, timerType, this);
+ int timerId = d->threadData->eventDispatcher.loadRelaxed()->registerTimer(interval, timerType, this);
if (!d->extraData)
d->extraData = new QObjectPrivate::ExtraData;
d->extraData->runningTimers.append(timerId);
@@ -1768,7 +1773,7 @@ void QObject::killTimer(int id)
}
if (d->threadData->hasEventDispatcher())
- d->threadData->eventDispatcher.load()->unregisterTimer(id);
+ d->threadData->eventDispatcher.loadRelaxed()->unregisterTimer(id);
d->extraData->runningTimers.remove(at);
QAbstractEventDispatcherPrivate::releaseTimerId(id);
@@ -2069,8 +2074,25 @@ void QObjectPrivate::deleteChildren()
void QObjectPrivate::setParent_helper(QObject *o)
{
Q_Q(QObject);
+ Q_ASSERT_X(q != o, Q_FUNC_INFO, "Cannot parent a QObject to itself");
+#ifdef QT_DEBUG
+ const auto checkForParentChildLoops = qScopeGuard([&](){
+ int depth = 0;
+ auto p = parent;
+ while (p) {
+ if (++depth == CheckForParentChildLoopsWarnDepth) {
+ qWarning("QObject %p (class: '%s', object name: '%s') may have a loop in its parent-child chain; "
+ "this is undefined behavior",
+ q, q->metaObject()->className(), qPrintable(q->objectName()));
+ }
+ p = p->parent();
+ }
+ });
+#endif
+
if (o == parent)
return;
+
if (parent) {
QObjectPrivate *parentD = parent->d_func();
if (parentD->isDeletingChildren && wasDeleted
@@ -2420,7 +2442,7 @@ QObject *QObject::sender() const
Q_D(const QObject);
QBasicMutexLocker locker(signalSlotLock(this));
- QObjectPrivate::ConnectionData *cd = d->connections.load();
+ QObjectPrivate::ConnectionData *cd = d->connections.loadRelaxed();
if (!cd || !cd->currentSender)
return nullptr;
@@ -2462,7 +2484,7 @@ int QObject::senderSignalIndex() const
Q_D(const QObject);
QBasicMutexLocker locker(signalSlotLock(this));
- QObjectPrivate::ConnectionData *cd = d->connections.load();
+ QObjectPrivate::ConnectionData *cd = d->connections.loadRelaxed();
if (!cd || !cd->currentSender)
return -1;
@@ -2525,13 +2547,13 @@ int QObject::receivers(const char *signal) const
signal_index);
}
- QObjectPrivate::ConnectionData *cd = d->connections.load();
+ QObjectPrivate::ConnectionData *cd = d->connections.loadRelaxed();
QBasicMutexLocker locker(signalSlotLock(this));
if (cd && signal_index < cd->signalVectorCount()) {
- const QObjectPrivate::Connection *c = cd->signalVector.load()->at(signal_index).first.load();
+ const QObjectPrivate::Connection *c = cd->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed();
while (c) {
- receivers += c->receiver.load() ? 1 : 0;
- c = c->nextConnectionList.load();
+ receivers += c->receiver.loadRelaxed() ? 1 : 0;
+ c = c->nextConnectionList.loadRelaxed();
}
}
}
@@ -3340,17 +3362,17 @@ QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender,
QOrderedMutexLocker locker(signalSlotLock(sender),
signalSlotLock(receiver));
- QObjectPrivate::ConnectionData *scd = QObjectPrivate::get(s)->connections.load();
+ QObjectPrivate::ConnectionData *scd = QObjectPrivate::get(s)->connections.loadRelaxed();
if (type & Qt::UniqueConnection && scd) {
if (scd->signalVectorCount() > signal_index) {
- const QObjectPrivate::Connection *c2 = scd->signalVector.load()->at(signal_index).first.load();
+ const QObjectPrivate::Connection *c2 = scd->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed();
int method_index_absolute = method_index + method_offset;
while (c2) {
- if (!c2->isSlotObject && c2->receiver.load() == receiver && c2->method() == method_index_absolute)
+ if (!c2->isSlotObject && c2->receiver.loadRelaxed() == receiver && c2->method() == method_index_absolute)
return nullptr;
- c2 = c2->nextConnectionList.load();
+ c2 = c2->nextConnectionList.loadRelaxed();
}
}
type &= Qt::UniqueConnection - 1;
@@ -3359,15 +3381,15 @@ QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender,
QScopedPointer<QObjectPrivate::Connection> c(new QObjectPrivate::Connection);
c->sender = s;
c->signal_index = signal_index;
- c->receiver.store(r);
+ c->receiver.storeRelaxed(r);
QThreadData *td = r->d_func()->threadData;
td->ref();
- c->receiverThreadData.store(td);
+ c->receiverThreadData.storeRelaxed(td);
c->method_relative = method_index;
c->method_offset = method_offset;
c->connectionType = type;
c->isSlotObject = false;
- c->argumentTypes.store(types);
+ c->argumentTypes.storeRelaxed(types);
c->callFunction = callFunction;
QObjectPrivate::get(s)->addConnection(signal_index, c.data());
@@ -3420,9 +3442,9 @@ bool QMetaObjectPrivate::disconnectHelper(QObjectPrivate::ConnectionData *connec
bool success = false;
auto &connectionList = connections->connectionsForSignal(signalIndex);
- auto *c = connectionList.first.load();
+ auto *c = connectionList.first.loadRelaxed();
while (c) {
- QObject *r = c->receiver.load();
+ QObject *r = c->receiver.loadRelaxed();
if (r && (receiver == nullptr || (r == receiver
&& (method_index < 0 || (!c->isSlotObject && c->method() == method_index))
&& (slot == nullptr || (c->isSlotObject && c->slotObj->compare(slot)))))) {
@@ -3433,7 +3455,7 @@ bool QMetaObjectPrivate::disconnectHelper(QObjectPrivate::ConnectionData *connec
// need to relock this receiver and sender in the correct order
needToUnlock = QOrderedMutexLocker::relock(senderMutex, receiverMutex);
}
- if (c->receiver.load())
+ if (c->receiver.loadRelaxed())
connections->removeConnection(c);
if (needToUnlock)
@@ -3444,7 +3466,7 @@ bool QMetaObjectPrivate::disconnectHelper(QObjectPrivate::ConnectionData *connec
if (disconnectType == DisconnectOne)
return success;
}
- c = c->nextConnectionList.load();
+ c = c->nextConnectionList.loadRelaxed();
}
return success;
}
@@ -3466,7 +3488,7 @@ bool QMetaObjectPrivate::disconnect(const QObject *sender,
QBasicMutex *senderMutex = signalSlotLock(sender);
QBasicMutexLocker locker(senderMutex);
- QObjectPrivate::ConnectionData *scd = QObjectPrivate::get(s)->connections.load();
+ QObjectPrivate::ConnectionData *scd = QObjectPrivate::get(s)->connections.loadRelaxed();
if (!scd)
return false;
@@ -3608,7 +3630,7 @@ void QMetaObject::connectSlotsByName(QObject *o)
*/
static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv)
{
- const int *argumentTypes = c->argumentTypes.load();
+ const int *argumentTypes = c->argumentTypes.loadRelaxed();
if (!argumentTypes) {
QMetaMethod m = QMetaObjectPrivate::signal(sender->metaObject(), signal);
argumentTypes = queuedConnectionTypes(m.parameterTypes());
@@ -3617,7 +3639,7 @@ static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connect
if (!c->argumentTypes.testAndSetOrdered(0, argumentTypes)) {
if (argumentTypes != &DIRECT_CONNECTION_ONLY)
delete [] argumentTypes;
- argumentTypes = c->argumentTypes.load();
+ argumentTypes = c->argumentTypes.loadRelaxed();
}
}
if (argumentTypes == &DIRECT_CONNECTION_ONLY) // cannot activate
@@ -3640,8 +3662,8 @@ static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connect
args[n] = QMetaType::create(types[n], argv[n]);
}
- QBasicMutexLocker locker(signalSlotLock(c->receiver.load()));
- if (!c->receiver.load()) {
+ QBasicMutexLocker locker(signalSlotLock(c->receiver.loadRelaxed()));
+ if (!c->receiver.loadRelaxed()) {
// the connection has been disconnected before we got the lock
locker.unlock();
for (int n = 1; n < nargs; ++n)
@@ -3654,7 +3676,7 @@ static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connect
QMetaCallEvent *ev = c->isSlotObject ?
new QMetaCallEvent(c->slotObj, sender, signal, nargs, types, args) :
new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction, sender, signal, nargs, types, args);
- QCoreApplication::postEvent(c->receiver.load(), ev);
+ QCoreApplication::postEvent(c->receiver.loadRelaxed(), ev);
}
template <bool callbacks_enabled>
@@ -3674,7 +3696,7 @@ void doActivate(QObject *sender, int signal_index, void **argv)
signal_index, argv);
}
- const QSignalSpyCallbackSet *signal_spy_set = callbacks_enabled ? qt_signal_spy_callback_set.load() : nullptr;
+ const QSignalSpyCallbackSet *signal_spy_set = callbacks_enabled ? qt_signal_spy_callback_set.loadAcquire() : nullptr;
void *empty_argv[] = { nullptr };
if (!argv)
@@ -3695,8 +3717,8 @@ void doActivate(QObject *sender, int signal_index, void **argv)
bool senderDeleted = false;
{
Q_ASSERT(sp->connections);
- QObjectPrivate::ConnectionDataPointer connections(sp->connections.load());
- QObjectPrivate::SignalVector *signalVector = connections->signalVector.load();
+ QObjectPrivate::ConnectionDataPointer connections(sp->connections.loadRelaxed());
+ QObjectPrivate::SignalVector *signalVector = connections->signalVector.loadRelaxed();
const QObjectPrivate::ConnectionList *list;
if (signal_index < signalVector->count())
@@ -3705,32 +3727,32 @@ void doActivate(QObject *sender, int signal_index, void **argv)
list = &signalVector->at(-1);
Qt::HANDLE currentThreadId = QThread::currentThreadId();
- bool inSenderThread = currentThreadId == QObjectPrivate::get(sender)->threadData->threadId.load();
+ bool inSenderThread = currentThreadId == QObjectPrivate::get(sender)->threadData->threadId.loadRelaxed();
// We need to check against the highest connection id to ensure that signals added
// during the signal emission are not emitted in this emission.
- uint highestConnectionId = connections->currentConnectionId.load();
+ uint highestConnectionId = connections->currentConnectionId.loadRelaxed();
do {
- QObjectPrivate::Connection *c = list->first.load();
+ QObjectPrivate::Connection *c = list->first.loadRelaxed();
if (!c)
continue;
do {
- QObject * const receiver = c->receiver.load();
+ QObject * const receiver = c->receiver.loadRelaxed();
if (!receiver)
continue;
- QThreadData *td = c->receiverThreadData.load();
+ QThreadData *td = c->receiverThreadData.loadRelaxed();
if (!td)
continue;
bool receiverInSameThread;
if (inSenderThread) {
- receiverInSameThread = currentThreadId == td->threadId.load();
+ receiverInSameThread = currentThreadId == td->threadId.loadRelaxed();
} else {
// need to lock before reading the threadId, because moveToThread() could interfere
QMutexLocker lock(signalSlotLock(receiver));
- receiverInSameThread = currentThreadId == td->threadId.load();
+ receiverInSameThread = currentThreadId == td->threadId.loadRelaxed();
}
@@ -3803,17 +3825,17 @@ void doActivate(QObject *sender, int signal_index, void **argv)
if (callbacks_enabled && signal_spy_set->slot_end_callback != nullptr)
signal_spy_set->slot_end_callback(receiver, method);
}
- } while ((c = c->nextConnectionList.load()) != nullptr && c->id <= highestConnectionId);
+ } while ((c = c->nextConnectionList.loadRelaxed()) != nullptr && c->id <= highestConnectionId);
} while (list != &signalVector->at(-1) &&
//start over for all signals;
((list = &signalVector->at(-1)), true));
- if (connections->currentConnectionId.load() == 0)
+ if (connections->currentConnectionId.loadRelaxed() == 0)
senderDeleted = true;
}
if (!senderDeleted)
- sp->connections.load()->cleanOrphanedConnections(sender);
+ sp->connections.loadRelaxed()->cleanOrphanedConnections(sender);
if (callbacks_enabled && signal_spy_set->signal_end_callback != nullptr)
signal_spy_set->signal_end_callback(sender, signal_index);
@@ -3827,7 +3849,7 @@ void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_sign
{
int signal_index = local_signal_index + QMetaObjectPrivate::signalOffset(m);
- if (Q_UNLIKELY(qt_signal_spy_callback_set.load()))
+ if (Q_UNLIKELY(qt_signal_spy_callback_set.loadRelaxed()))
doActivate<true>(sender, signal_index, argv);
else
doActivate<false>(sender, signal_index, argv);
@@ -3840,7 +3862,7 @@ void QMetaObject::activate(QObject *sender, int signalOffset, int local_signal_i
{
int signal_index = signalOffset + local_signal_index;
- if (Q_UNLIKELY(qt_signal_spy_callback_set.load()))
+ if (Q_UNLIKELY(qt_signal_spy_callback_set.loadRelaxed()))
doActivate<true>(sender, signal_index, argv);
else
doActivate<false>(sender, signal_index, argv);
@@ -4109,11 +4131,11 @@ void QObject::dumpObjectInfo() const
// first, look for connections where this object is the sender
qDebug(" SIGNALS OUT");
- QObjectPrivate::ConnectionData *cd = d->connections.load();
+ QObjectPrivate::ConnectionData *cd = d->connections.loadRelaxed();
if (cd && cd->signalVectorCount()) {
- QObjectPrivate::SignalVector *signalVector = cd->signalVector.load();
+ QObjectPrivate::SignalVector *signalVector = cd->signalVector.loadRelaxed();
for (int signal_index = 0; signal_index < signalVector->count(); ++signal_index) {
- const QObjectPrivate::Connection *c = signalVector->at(signal_index).first.load();
+ const QObjectPrivate::Connection *c = signalVector->at(signal_index).first.loadRelaxed();
if (!c)
continue;
const QMetaMethod signal = QMetaObjectPrivate::signal(metaObject(), signal_index);
@@ -4121,23 +4143,23 @@ void QObject::dumpObjectInfo() const
// receivers
while (c) {
- if (!c->receiver.load()) {
+ if (!c->receiver.loadRelaxed()) {
qDebug(" <Disconnected receiver>");
- c = c->nextConnectionList.load();
+ c = c->nextConnectionList.loadRelaxed();
continue;
}
if (c->isSlotObject) {
qDebug(" <functor or function pointer>");
- c = c->nextConnectionList.load();
+ c = c->nextConnectionList.loadRelaxed();
continue;
}
- const QMetaObject *receiverMetaObject = c->receiver.load()->metaObject();
+ const QMetaObject *receiverMetaObject = c->receiver.loadRelaxed()->metaObject();
const QMetaMethod method = receiverMetaObject->method(c->method());
qDebug(" --> %s::%s %s",
receiverMetaObject->className(),
- c->receiver.load()->objectName().isEmpty() ? "unnamed" : qPrintable(c->receiver.load()->objectName()),
+ c->receiver.loadRelaxed()->objectName().isEmpty() ? "unnamed" : qPrintable(c->receiver.loadRelaxed()->objectName()),
method.methodSignature().constData());
- c = c->nextConnectionList.load();
+ c = c->nextConnectionList.loadRelaxed();
}
}
} else {
@@ -4888,17 +4910,17 @@ QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int s
QOrderedMutexLocker locker(signalSlotLock(sender),
signalSlotLock(receiver));
- if (type & Qt::UniqueConnection && slot && QObjectPrivate::get(s)->connections.load()) {
- QObjectPrivate::ConnectionData *connections = QObjectPrivate::get(s)->connections.load();
+ if (type & Qt::UniqueConnection && slot && QObjectPrivate::get(s)->connections.loadRelaxed()) {
+ QObjectPrivate::ConnectionData *connections = QObjectPrivate::get(s)->connections.loadRelaxed();
if (connections->signalVectorCount() > signal_index) {
- const QObjectPrivate::Connection *c2 = connections->signalVector.load()->at(signal_index).first.load();
+ const QObjectPrivate::Connection *c2 = connections->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed();
while (c2) {
- if (c2->receiver.load() == receiver && c2->isSlotObject && c2->slotObj->compare(slot)) {
+ if (c2->receiver.loadRelaxed() == receiver && c2->isSlotObject && c2->slotObj->compare(slot)) {
slotObj->destroyIfLastRef();
return QMetaObject::Connection();
}
- c2 = c2->nextConnectionList.load();
+ c2 = c2->nextConnectionList.loadRelaxed();
}
}
type = static_cast<Qt::ConnectionType>(type ^ Qt::UniqueConnection);
@@ -4909,13 +4931,13 @@ QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int s
c->signal_index = signal_index;
QThreadData *td = r->d_func()->threadData;
td->ref();
- c->receiverThreadData.store(td);
- c->receiver.store(r);
+ c->receiverThreadData.storeRelaxed(td);
+ c->receiver.storeRelaxed(r);
c->slotObj = slotObj;
c->connectionType = type;
c->isSlotObject = true;
if (types) {
- c->argumentTypes.store(types);
+ c->argumentTypes.storeRelaxed(types);
c->ownArgumentTypes = false;
}
@@ -4944,7 +4966,7 @@ bool QObject::disconnect(const QMetaObject::Connection &connection)
if (!c)
return false;
- QObject *receiver = c->receiver.load();
+ QObject *receiver = c->receiver.loadRelaxed();
if (!receiver)
return false;
@@ -4956,11 +4978,11 @@ bool QObject::disconnect(const QMetaObject::Connection &connection)
QOrderedMutexLocker locker(senderMutex, receiverMutex);
// load receiver once again and recheck to ensure nobody else has removed the connection in the meantime
- receiver = c->receiver.load();
+ receiver = c->receiver.loadRelaxed();
if (!receiver)
return false;
- connections = QObjectPrivate::get(c->sender)->connections.load();
+ connections = QObjectPrivate::get(c->sender)->connections.loadRelaxed();
Q_ASSERT(connections);
connections->removeConnection(c);
}
@@ -5152,7 +5174,7 @@ bool QMetaObject::Connection::isConnected_helper() const
Q_ASSERT(d_ptr); // we're only called from operator RestrictedBool() const
QObjectPrivate::Connection *c = static_cast<QObjectPrivate::Connection *>(d_ptr);
- return c->receiver.load();
+ return c->receiver.loadRelaxed();
}
diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h
index 12512e74c5..7f72b69c1a 100644
--- a/src/corelib/kernel/qobject.h
+++ b/src/corelib/kernel/qobject.h
@@ -113,12 +113,17 @@ public:
int postedEvents;
QDynamicMetaObjectData *metaObject;
QMetaObject *dynamicMetaObject() const;
+
+#ifdef QT_DEBUG
+ enum { CheckForParentChildLoopsWarnDepth = 4096 };
+#endif
};
class Q_CORE_EXPORT QObject
{
Q_OBJECT
+
Q_PROPERTY(QString objectName READ objectName WRITE setObjectName NOTIFY objectNameChanged)
Q_DECLARE_PRIVATE(QObject)
@@ -129,7 +134,7 @@ public:
virtual bool event(QEvent *event);
virtual bool eventFilter(QObject *watched, QEvent *event);
-#if defined(QT_NO_TRANSLATION)
+#if defined(QT_NO_TRANSLATION) || defined(Q_CLANG_QDOC)
static QString tr(const char *sourceText, const char * = nullptr, int = -1)
{ return QString::fromUtf8(sourceText); }
#if QT_DEPRECATED_SINCE(5, 0)
diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h
index e6e57b29b9..1953aea21e 100644
--- a/src/corelib/kernel/qobject_p.h
+++ b/src/corelib/kernel/qobject_p.h
@@ -184,7 +184,7 @@ public:
}
void deref() {
if (!ref_.deref()) {
- Q_ASSERT(!receiver.load());
+ Q_ASSERT(!receiver.loadRelaxed());
Q_ASSERT(!isSlotObject);
delete this;
}
@@ -202,7 +202,7 @@ public:
: receiver(receiver), sender(sender), signal(signal)
{
if (receiver) {
- ConnectionData *cd = receiver->d_func()->connections.load();
+ ConnectionData *cd = receiver->d_func()->connections.loadRelaxed();
previous = cd->currentSender;
cd->currentSender = this;
}
@@ -210,7 +210,7 @@ public:
~Sender()
{
if (receiver)
- receiver->d_func()->connections.load()->currentSender = previous;
+ receiver->d_func()->connections.loadRelaxed()->currentSender = previous;
}
void receiverDeleted()
{
@@ -268,8 +268,8 @@ public:
~ConnectionData()
{
- deleteOrphaned(orphaned.load());
- SignalVector *v = signalVector.load();
+ deleteOrphaned(orphaned.loadRelaxed());
+ SignalVector *v = signalVector.loadRelaxed();
if (v)
free(v);
}
@@ -279,18 +279,18 @@ public:
void removeConnection(Connection *c);
void cleanOrphanedConnections(QObject *sender)
{
- if (orphaned.load() && ref == 1)
+ if (orphaned.loadRelaxed() && ref == 1)
cleanOrphanedConnectionsImpl(sender);
}
void cleanOrphanedConnectionsImpl(QObject *sender);
ConnectionList &connectionsForSignal(int signal)
{
- return signalVector.load()->at(signal);
+ return signalVector.loadRelaxed()->at(signal);
}
void resizeSignalVector(uint size) {
- SignalVector *vector = this->signalVector.load();
+ SignalVector *vector = this->signalVector.loadRelaxed();
if (vector && vector->allocated > size)
return;
size = (size + 7) & ~7;
@@ -305,14 +305,14 @@ public:
newVector->next = nullptr;
newVector->allocated = size;
- signalVector.store(newVector);
+ signalVector.storeRelaxed(newVector);
if (vector) {
- vector->nextInOrphanList = orphaned.load();
- orphaned.store(ConnectionOrSignalVector::fromSignalVector(vector));
+ vector->nextInOrphanList = orphaned.loadRelaxed();
+ orphaned.storeRelaxed(ConnectionOrSignalVector::fromSignalVector(vector));
}
}
int signalVectorCount() const {
- return signalVector ? signalVector.load()->count() : -1;
+ return signalVector ? signalVector.loadRelaxed()->count() : -1;
}
static void deleteOrphaned(ConnectionOrSignalVector *c);
@@ -366,11 +366,11 @@ public:
void ensureConnectionData()
{
- if (connections.load())
+ if (connections.loadRelaxed())
return;
ConnectionData *cd = new ConnectionData;
cd->ref.ref();
- connections.store(cd);
+ connections.storeRelaxed(cd);
}
public:
ExtraData *extraData; // extra data set by the user
diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h
index 0bceab6fb4..4d5ac4dcb2 100644
--- a/src/corelib/kernel/qobjectdefs.h
+++ b/src/corelib/kernel/qobjectdefs.h
@@ -138,6 +138,10 @@ class QString;
# define QT_TR_FUNCTIONS
#endif
+#ifdef Q_CLANG_QDOC
+#define QT_TR_FUNCTIONS
+#endif
+
// ### Qt6: remove
#define Q_OBJECT_CHECK /* empty, unused since Qt 5.2 */
diff --git a/src/corelib/kernel/qpointer.h b/src/corelib/kernel/qpointer.h
index 80faef2990..7052bcf0d4 100644
--- a/src/corelib/kernel/qpointer.h
+++ b/src/corelib/kernel/qpointer.h
@@ -143,7 +143,8 @@ template<typename T>
QPointer<T>
qPointerFromVariant(const QVariant &variant)
{
- return QPointer<T>(qobject_cast<T*>(QtSharedPointer::weakPointerFromVariant_internal(variant).internalData()));
+ const auto wp = QtSharedPointer::weakPointerFromVariant_internal(variant);
+ return QPointer<T>{qobject_cast<T*>(QtPrivate::EnableInternalData::internalData(wp))};
}
template <class T>
diff --git a/src/corelib/kernel/qpoll.cpp b/src/corelib/kernel/qpoll.cpp
index 8e93c4d363..8b87bfe79d 100644
--- a/src/corelib/kernel/qpoll.cpp
+++ b/src/corelib/kernel/qpoll.cpp
@@ -39,6 +39,10 @@
#include "qcore_unix_p.h"
+#ifdef Q_OS_RTEMS
+#include <rtems/rtems_bsdnet_internal.h>
+#endif
+
QT_BEGIN_NAMESPACE
#define QT_POLL_READ_MASK (POLLIN | POLLRDNORM)
@@ -135,6 +139,11 @@ static inline int qt_poll_sweep(struct pollfd *fds, nfds_t nfds,
static inline bool qt_poll_is_bad_fd(int fd)
{
+#ifdef Q_OS_RTEMS
+ if (!rtems_bsdnet_fdToSocket(fd))
+ return true;
+#endif
+
int ret;
EINTR_LOOP(ret, fcntl(fd, F_GETFD));
return (ret == -1 && errno == EBADF);
diff --git a/src/corelib/kernel/qsharedmemory.cpp b/src/corelib/kernel/qsharedmemory.cpp
index c952655cb8..39f3002394 100644
--- a/src/corelib/kernel/qsharedmemory.cpp
+++ b/src/corelib/kernel/qsharedmemory.cpp
@@ -150,11 +150,18 @@ QSharedMemoryPrivate::makePlatformSafeKey(const QString &key,
\sa setKey()
*/
+
+#ifndef QT_NO_QOBJECT
QSharedMemory::QSharedMemory(QObject *parent)
: QObject(*new QSharedMemoryPrivate, parent)
{
}
-
+#else
+QSharedMemory::QSharedMemory()
+ : d_ptr(new QSharedMemoryPrivate)
+{
+}
+#endif
/*!
Constructs a shared memory object with the given \a parent and with
its key set to \a key. Because its key is set, its create() and
@@ -162,11 +169,19 @@ QSharedMemory::QSharedMemory(QObject *parent)
\sa setKey(), create(), attach()
*/
+#ifndef QT_NO_QOBJECT
QSharedMemory::QSharedMemory(const QString &key, QObject *parent)
: QObject(*new QSharedMemoryPrivate, parent)
{
setKey(key);
}
+#else
+QSharedMemory::QSharedMemory(const QString &key)
+ : d_ptr(new QSharedMemoryPrivate)
+{
+ setKey(key);
+}
+#endif
/*!
The destructor clears the key, which forces the shared memory object
@@ -604,4 +619,6 @@ QString QSharedMemory::errorString() const
QT_END_NAMESPACE
+#ifndef QT_NO_QOBJECT
#include "moc_qsharedmemory.cpp"
+#endif
diff --git a/src/corelib/kernel/qsharedmemory.h b/src/corelib/kernel/qsharedmemory.h
index 67cf52ac17..6236c6aa4c 100644
--- a/src/corelib/kernel/qsharedmemory.h
+++ b/src/corelib/kernel/qsharedmemory.h
@@ -40,8 +40,14 @@
#ifndef QSHAREDMEMORY_H
#define QSHAREDMEMORY_H
-#include <QtCore/qobject.h>
-
+#include <QtCore/qglobal.h>
+#ifndef QT_NO_QOBJECT
+# include <QtCore/qobject.h>
+#else
+# include <QtCore/qobjectdefs.h>
+# include <QtCore/qscopedpointer.h>
+# include <QtCore/qstring.h>
+#endif
QT_BEGIN_NAMESPACE
@@ -49,9 +55,14 @@ QT_BEGIN_NAMESPACE
class QSharedMemoryPrivate;
-class Q_CORE_EXPORT QSharedMemory : public QObject
+class Q_CORE_EXPORT QSharedMemory
+#ifndef QT_NO_QOBJECT
+ : public QObject
+#endif
{
+#ifndef QT_NO_QOBJECT
Q_OBJECT
+#endif
Q_DECLARE_PRIVATE(QSharedMemory)
public:
@@ -74,8 +85,17 @@ public:
UnknownError
};
+#ifndef QT_NO_QOBJECT
QSharedMemory(QObject *parent = nullptr);
QSharedMemory(const QString &key, QObject *parent = nullptr);
+#else
+ QSharedMemory();
+ QSharedMemory(const QString &key);
+ static QString tr(const char * str)
+ {
+ return QString::fromLatin1(str);
+ }
+#endif
~QSharedMemory();
void setKey(const QString &key);
@@ -104,6 +124,9 @@ public:
private:
Q_DISABLE_COPY(QSharedMemory)
+#ifdef QT_NO_QOBJECT
+ QScopedPointer<QSharedMemoryPrivate> d_ptr;
+#endif
};
#endif // QT_NO_SHAREDMEMORY
diff --git a/src/corelib/kernel/qsharedmemory_p.h b/src/corelib/kernel/qsharedmemory_p.h
index bf7c42dc62..e6e989abda 100644
--- a/src/corelib/kernel/qsharedmemory_p.h
+++ b/src/corelib/kernel/qsharedmemory_p.h
@@ -67,7 +67,10 @@ namespace QSharedMemoryPrivate
#else
#include "qsystemsemaphore.h"
-#include "private/qobject_p.h"
+
+#ifndef QT_NO_QOBJECT
+# include "private/qobject_p.h"
+#endif
#if !defined(Q_OS_WIN) && !defined(Q_OS_ANDROID) && !defined(Q_OS_INTEGRITY) && !defined(Q_OS_RTEMS)
# include <sys/sem.h>
@@ -107,9 +110,14 @@ private:
};
#endif // QT_NO_SYSTEMSEMAPHORE
-class Q_AUTOTEST_EXPORT QSharedMemoryPrivate : public QObjectPrivate
+class Q_AUTOTEST_EXPORT QSharedMemoryPrivate
+#ifndef QT_NO_QOBJECT
+ : public QObjectPrivate
+#endif
{
+#ifndef QT_NO_QOBJECT
Q_DECLARE_PUBLIC(QSharedMemory)
+#endif
public:
QSharedMemoryPrivate();
diff --git a/src/corelib/kernel/qsharedmemory_unix.cpp b/src/corelib/kernel/qsharedmemory_unix.cpp
index d1d44747e0..f6d7e78441 100644
--- a/src/corelib/kernel/qsharedmemory_unix.cpp
+++ b/src/corelib/kernel/qsharedmemory_unix.cpp
@@ -64,8 +64,11 @@
#ifndef QT_NO_SHAREDMEMORY
QT_BEGIN_NAMESPACE
-QSharedMemoryPrivate::QSharedMemoryPrivate()
- : QObjectPrivate(), memory(0), size(0), error(QSharedMemory::NoError),
+QSharedMemoryPrivate::QSharedMemoryPrivate() :
+#ifndef QT_NO_QOBJECT
+ QObjectPrivate(),
+#endif
+ memory(0), size(0), error(QSharedMemory::NoError),
#ifndef QT_NO_SYSTEMSEMAPHORE
systemSemaphore(QString()), lockedByMe(false),
#endif
diff --git a/src/corelib/kernel/qsharedmemory_win.cpp b/src/corelib/kernel/qsharedmemory_win.cpp
index c1dba30e2b..02de2339a5 100644
--- a/src/corelib/kernel/qsharedmemory_win.cpp
+++ b/src/corelib/kernel/qsharedmemory_win.cpp
@@ -47,7 +47,10 @@ QT_BEGIN_NAMESPACE
#ifndef QT_NO_SHAREDMEMORY
-QSharedMemoryPrivate::QSharedMemoryPrivate() : QObjectPrivate(),
+QSharedMemoryPrivate::QSharedMemoryPrivate() :
+#ifndef QT_NO_QOBJECT
+ QObjectPrivate(),
+#endif
memory(0), size(0), error(QSharedMemory::NoError),
systemSemaphore(QString()), lockedByMe(false), hand(0)
{
diff --git a/src/corelib/kernel/qsocketnotifier.cpp b/src/corelib/kernel/qsocketnotifier.cpp
index 6ff8268978..2a246b1204 100644
--- a/src/corelib/kernel/qsocketnotifier.cpp
+++ b/src/corelib/kernel/qsocketnotifier.cpp
@@ -152,7 +152,7 @@ QSocketNotifier::QSocketNotifier(qintptr socket, Type type, QObject *parent)
else if (!d->threadData->hasEventDispatcher())
qWarning("QSocketNotifier: Can only be used with threads started with QThread");
else
- d->threadData->eventDispatcher.load()->registerSocketNotifier(this);
+ d->threadData->eventDispatcher.loadRelaxed()->registerSocketNotifier(this);
}
/*!
@@ -241,9 +241,9 @@ void QSocketNotifier::setEnabled(bool enable)
return;
}
if (d->snenabled)
- d->threadData->eventDispatcher.load()->registerSocketNotifier(this);
+ d->threadData->eventDispatcher.loadRelaxed()->registerSocketNotifier(this);
else
- d->threadData->eventDispatcher.load()->unregisterSocketNotifier(this);
+ d->threadData->eventDispatcher.loadRelaxed()->unregisterSocketNotifier(this);
}
diff --git a/src/corelib/kernel/qsystemerror.cpp b/src/corelib/kernel/qsystemerror.cpp
index 9d0394e4a5..a735e12925 100644
--- a/src/corelib/kernel/qsystemerror.cpp
+++ b/src/corelib/kernel/qsystemerror.cpp
@@ -112,7 +112,7 @@ static QString windowsErrorString(int errorCode)
static QString standardLibraryErrorString(int errorCode)
{
- const char *s = 0;
+ const char *s = nullptr;
QString ret;
switch (errorCode) {
case 0:
diff --git a/src/corelib/kernel/qsystemsemaphore_systemv.cpp b/src/corelib/kernel/qsystemsemaphore_systemv.cpp
index 9e438ae2a6..2c38d74d2d 100644
--- a/src/corelib/kernel/qsystemsemaphore_systemv.cpp
+++ b/src/corelib/kernel/qsystemsemaphore_systemv.cpp
@@ -76,7 +76,7 @@ key_t QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode mode)
#if QT_CONFIG(translation)
QCoreApplication::tr("%1: key is empty", "QSystemSemaphore")
#else
- QString::fromLatin1("%1: key is empty")
+ QLatin1String("%1: key is empty")
#endif
.arg(QLatin1String("QSystemSemaphore::handle:"));
error = QSystemSemaphore::KeyError;
@@ -94,7 +94,7 @@ key_t QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode mode)
#if QT_CONFIG(translation)
QCoreApplication::tr("%1: unable to make key", "QSystemSemaphore")
#else
- QString::fromLatin1("%1: unable to make key")
+ QLatin1String("%1: unable to make key")
#endif
.arg(QLatin1String("QSystemSemaphore::handle:"));
error = QSystemSemaphore::KeyError;
@@ -111,7 +111,7 @@ key_t QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode mode)
#if QT_CONFIG(translation)
QCoreApplication::tr("%1: ftok failed", "QSystemSemaphore")
#else
- QString::fromLatin1("%1: ftok failed")
+ QLatin1String("%1: ftok failed")
#endif
.arg(QLatin1String("QSystemSemaphore::handle:"));
error = QSystemSemaphore::KeyError;
diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp
index 1ac47f3972..511dc3c81c 100644
--- a/src/corelib/kernel/qvariant.cpp
+++ b/src/corelib/kernel/qvariant.cpp
@@ -119,19 +119,19 @@ namespace { // annonymous used to hide QVariant handlers
static void construct(QVariant::Private *x, const void *copy)
{
QVariantConstructor<CoreTypesFilter> constructor(x, copy);
- QMetaTypeSwitcher::switcher<void>(constructor, x->type, 0);
+ QMetaTypeSwitcher::switcher<void>(constructor, x->type);
}
static void clear(QVariant::Private *d)
{
QVariantDestructor<CoreTypesFilter> cleaner(d);
- QMetaTypeSwitcher::switcher<void>(cleaner, d->type, 0);
+ QMetaTypeSwitcher::switcher<void>(cleaner, d->type);
}
static bool isNull(const QVariant::Private *d)
{
QVariantIsNull<CoreTypesFilter> isNull(d);
- return QMetaTypeSwitcher::switcher<bool>(isNull, d->type, 0);
+ return QMetaTypeSwitcher::switcher<bool>(isNull, d->type);
}
/*!
@@ -143,7 +143,7 @@ static bool isNull(const QVariant::Private *d)
static bool compare(const QVariant::Private *a, const QVariant::Private *b)
{
QVariantComparator<CoreTypesFilter> comparator(a, b);
- return QMetaTypeSwitcher::switcher<bool>(comparator, a->type, 0);
+ return QMetaTypeSwitcher::switcher<bool>(comparator, a->type);
}
/*!
@@ -1401,7 +1401,7 @@ static void streamDebug(QDebug dbg, const QVariant &v)
{
QVariant::Private *d = const_cast<QVariant::Private *>(&v.data_ptr());
QVariantDebugStream<CoreTypesFilter> stream(dbg, d);
- QMetaTypeSwitcher::switcher<void>(stream, d->type, 0);
+ QMetaTypeSwitcher::switcher<void>(stream, d->type);
}
#endif
@@ -1410,16 +1410,16 @@ const QVariant::Handler qt_kernel_variant_handler = {
clear,
isNull,
#ifndef QT_NO_DATASTREAM
- 0,
- 0,
+ nullptr,
+ nullptr,
#endif
compare,
convert,
- 0,
+ nullptr,
#if !defined(QT_NO_DEBUG_STREAM)
streamDebug
#else
- 0
+ nullptr
#endif
};
@@ -1436,16 +1436,16 @@ const QVariant::Handler qt_dummy_variant_handler = {
dummyClear,
dummyIsNull,
#ifndef QT_NO_DATASTREAM
- 0,
- 0,
+ nullptr,
+ nullptr,
#endif
dummyCompare,
dummyConvert,
- 0,
+ nullptr,
#if !defined(QT_NO_DEBUG_STREAM)
dummyStreamDebug
#else
- 0
+ nullptr
#endif
};
@@ -1555,16 +1555,16 @@ const QVariant::Handler qt_custom_variant_handler = {
customClear,
customIsNull,
#ifndef QT_NO_DATASTREAM
- 0,
- 0,
+ nullptr,
+ nullptr,
#endif
customCompare,
customConvert,
- 0,
+ nullptr,
#if !defined(QT_NO_DEBUG_STREAM)
customStreamDebug
#else
- 0
+ nullptr
#endif
};
@@ -2125,7 +2125,7 @@ QVariant::QVariant(const char *val)
*/
QVariant::QVariant(Type type)
-{ create(type, 0); }
+{ create(type, nullptr); }
QVariant::QVariant(int typeId, const void *copy)
{ create(typeId, copy); d.is_null = false; }
@@ -2366,7 +2366,7 @@ QVariant& QVariant::operator=(const QVariant &variant)
void QVariant::detach()
{
- if (!d.is_shared || d.data.shared->ref.load() == 1)
+ if (!d.is_shared || d.data.shared->ref.loadRelaxed() == 1)
return;
Private dd;
@@ -2667,7 +2667,7 @@ inline T qVariantToHelper(const QVariant::Private &d, const HandlersManager &han
return ret;
}
- handlerManager[d.type]->convert(&d, targetType, &ret, 0);
+ handlerManager[d.type]->convert(&d, targetType, &ret, nullptr);
return ret;
}
@@ -3217,7 +3217,7 @@ bool QVariant::toBool() const
return d.data.b;
bool res = false;
- handlerManager[d.type]->convert(&d, Bool, &res, 0);
+ handlerManager[d.type]->convert(&d, Bool, &res, nullptr);
return res;
}
@@ -3735,7 +3735,7 @@ bool QVariant::convert(int targetTypeId)
if (!oldValue.canConvert(targetTypeId))
return false;
- create(targetTypeId, 0);
+ create(targetTypeId, nullptr);
// Fail if the value is not initialized or was forced null by a previous failed convert.
if (oldValue.d.is_null && oldValue.d.type != QMetaType::Nullptr)
return false;
@@ -3760,7 +3760,7 @@ bool QVariant::convert(int targetTypeId)
*/
bool QVariant::convert(const int type, void *ptr) const
{
- return handlerManager[type]->convert(&d, type, ptr, 0);
+ return handlerManager[type]->convert(&d, type, ptr, nullptr);
}
diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h
index e094ebff52..c8cb551863 100644
--- a/src/corelib/kernel/qvariant.h
+++ b/src/corelib/kernel/qvariant.h
@@ -577,7 +577,7 @@ Q_CORE_EXPORT QDataStream& operator<< (QDataStream& s, const QVariant::Type p);
#endif
inline bool QVariant::isDetached() const
-{ return !d.is_shared || d.data.shared->ref.load() == 1; }
+{ return !d.is_shared || d.data.shared->ref.loadRelaxed() == 1; }
#ifdef Q_QDOC
diff --git a/src/corelib/kernel/qwineventnotifier.cpp b/src/corelib/kernel/qwineventnotifier.cpp
index 3c73c0b851..d2ae9668fe 100644
--- a/src/corelib/kernel/qwineventnotifier.cpp
+++ b/src/corelib/kernel/qwineventnotifier.cpp
@@ -124,7 +124,7 @@ QWinEventNotifier::QWinEventNotifier(HANDLE hEvent, QObject *parent)
: QObject(*new QWinEventNotifierPrivate(hEvent, false), parent)
{
Q_D(QWinEventNotifier);
- QAbstractEventDispatcher *eventDispatcher = d->threadData->eventDispatcher.load();
+ QAbstractEventDispatcher *eventDispatcher = d->threadData->eventDispatcher.loadRelaxed();
if (Q_UNLIKELY(!eventDispatcher)) {
qWarning("QWinEventNotifier: Can only be used with threads started with QThread");
return;
@@ -197,7 +197,7 @@ void QWinEventNotifier::setEnabled(bool enable)
return;
d->enabled = enable;
- QAbstractEventDispatcher *eventDispatcher = d->threadData->eventDispatcher.load();
+ QAbstractEventDispatcher *eventDispatcher = d->threadData->eventDispatcher.loadRelaxed();
if (!eventDispatcher) { // perhaps application is shutting down
if (!enable && d->waitHandle != nullptr)
d->unregisterWaitObject();
@@ -256,7 +256,7 @@ void QWinEventNotifierPrivate::unregisterWaitObject()
static void CALLBACK wfsoCallback(void *context, BOOLEAN /*ignore*/)
{
QWinEventNotifierPrivate *nd = reinterpret_cast<QWinEventNotifierPrivate *>(context);
- QAbstractEventDispatcher *eventDispatcher = nd->threadData->eventDispatcher.load();
+ QAbstractEventDispatcher *eventDispatcher = nd->threadData->eventDispatcher.loadRelaxed();
// Happens when Q(Core)Application is destroyed before QWinEventNotifier.
// https://bugreports.qt.io/browse/QTBUG-70214
diff --git a/src/corelib/mimetypes/qmimetype.cpp b/src/corelib/mimetypes/qmimetype.cpp
index cf01c9503b..de450c68f4 100644
--- a/src/corelib/mimetypes/qmimetype.cpp
+++ b/src/corelib/mimetypes/qmimetype.cpp
@@ -253,7 +253,7 @@ QString QMimeType::name() const
*/
QString QMimeType::comment() const
{
- QMimeDatabasePrivate::instance()->loadMimeTypePrivate(*d);
+ QMimeDatabasePrivate::instance()->loadMimeTypePrivate(const_cast<QMimeTypePrivate&>(*d));
QStringList languageList;
languageList << QLocale().name();
@@ -294,7 +294,7 @@ QString QMimeType::comment() const
*/
QString QMimeType::genericIconName() const
{
- QMimeDatabasePrivate::instance()->loadGenericIcon(*d);
+ QMimeDatabasePrivate::instance()->loadGenericIcon(const_cast<QMimeTypePrivate&>(*d));
if (d->genericIconName.isEmpty()) {
// From the spec:
// If the generic icon name is empty (not specified by the mimetype definition)
@@ -311,6 +311,14 @@ QString QMimeType::genericIconName() const
return d->genericIconName;
}
+static QString make_default_icon_name_from_mimetype_name(QString iconName)
+{
+ const int slashindex = iconName.indexOf(QLatin1Char('/'));
+ if (slashindex != -1)
+ iconName[slashindex] = QLatin1Char('-');
+ return iconName;
+}
+
/*!
\property QMimeType::iconName
\brief the file name of an icon image that represents the MIME type
@@ -322,13 +330,9 @@ QString QMimeType::genericIconName() const
*/
QString QMimeType::iconName() const
{
- QMimeDatabasePrivate::instance()->loadIcon(*d);
+ QMimeDatabasePrivate::instance()->loadIcon(const_cast<QMimeTypePrivate&>(*d));
if (d->iconName.isEmpty()) {
- // Make default icon name from the mimetype name
- d->iconName = name();
- const int slashindex = d->iconName.indexOf(QLatin1Char('/'));
- if (slashindex != -1)
- d->iconName[slashindex] = QLatin1Char('-');
+ return make_default_icon_name_from_mimetype_name(name());
}
return d->iconName;
}
@@ -342,7 +346,7 @@ QString QMimeType::iconName() const
*/
QStringList QMimeType::globPatterns() const
{
- QMimeDatabasePrivate::instance()->loadMimeTypePrivate(*d);
+ QMimeDatabasePrivate::instance()->loadMimeTypePrivate(const_cast<QMimeTypePrivate&>(*d));
return d->globPatterns;
}
@@ -437,7 +441,7 @@ QStringList QMimeType::aliases() const
*/
QStringList QMimeType::suffixes() const
{
- QMimeDatabasePrivate::instance()->loadMimeTypePrivate(*d);
+ QMimeDatabasePrivate::instance()->loadMimeTypePrivate(const_cast<QMimeTypePrivate&>(*d));
QStringList result;
for (const QString &pattern : qAsConst(d->globPatterns)) {
@@ -480,7 +484,7 @@ QString QMimeType::preferredSuffix() const
*/
QString QMimeType::filterString() const
{
- QMimeDatabasePrivate::instance()->loadMimeTypePrivate(*d);
+ QMimeDatabasePrivate::instance()->loadMimeTypePrivate(const_cast<QMimeTypePrivate&>(*d));
QString filter;
if (!d->globPatterns.empty()) {
diff --git a/src/corelib/mimetypes/qmimetype.h b/src/corelib/mimetypes/qmimetype.h
index 6d6f9eb488..df1b60f2ce 100644
--- a/src/corelib/mimetypes/qmimetype.h
+++ b/src/corelib/mimetypes/qmimetype.h
@@ -48,11 +48,11 @@ QT_REQUIRE_CONFIG(mimetype);
#include <QtCore/qobjectdefs.h>
#include <QtCore/qshareddata.h>
#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
QT_BEGIN_NAMESPACE
class QMimeTypePrivate;
-class QStringList;
class QMimeType;
Q_CORE_EXPORT uint qHash(const QMimeType &key, uint seed = 0) noexcept;
diff --git a/src/corelib/plugin/qlibrary.cpp b/src/corelib/plugin/qlibrary.cpp
index a707ea64ee..a8db108eeb 100644
--- a/src/corelib/plugin/qlibrary.cpp
+++ b/src/corelib/plugin/qlibrary.cpp
@@ -405,10 +405,10 @@ inline void QLibraryStore::cleanup()
LibraryMap::Iterator it = data->libraryMap.begin();
for (; it != data->libraryMap.end(); ++it) {
QLibraryPrivate *lib = it.value();
- if (lib->libraryRefCount.load() == 1) {
- if (lib->libraryUnloadCount.load() > 0) {
+ if (lib->libraryRefCount.loadRelaxed() == 1) {
+ if (lib->libraryUnloadCount.loadRelaxed() > 0) {
Q_ASSERT(lib->pHnd);
- lib->libraryUnloadCount.store(1);
+ lib->libraryUnloadCount.storeRelaxed(1);
#ifdef __GLIBC__
// glibc has a bug in unloading from global destructors
// see https://bugzilla.novell.com/show_bug.cgi?id=622977
@@ -428,7 +428,7 @@ inline void QLibraryStore::cleanup()
for (QLibraryPrivate *lib : qAsConst(data->libraryMap)) {
if (lib)
qDebug() << "On QtCore unload," << lib->fileName << "was leaked, with"
- << lib->libraryRefCount.load() << "users";
+ << lib->libraryRefCount.loadRelaxed() << "users";
}
}
@@ -487,7 +487,7 @@ inline void QLibraryStore::releaseLibrary(QLibraryPrivate *lib)
}
// no one else is using
- Q_ASSERT(lib->libraryUnloadCount.load() == 0);
+ Q_ASSERT(lib->libraryUnloadCount.loadRelaxed() == 0);
if (Q_LIKELY(data) && !lib->fileName.isEmpty()) {
QLibraryPrivate *that = data->libraryMap.take(lib->fileName);
@@ -501,7 +501,7 @@ QLibraryPrivate::QLibraryPrivate(const QString &canonicalFileName, const QString
: pHnd(0), fileName(canonicalFileName), fullVersion(version), instance(0),
libraryRefCount(0), libraryUnloadCount(0), pluginState(MightBeAPlugin)
{
- loadHintsInt.store(loadHints);
+ loadHintsInt.storeRelaxed(loadHints);
if (canonicalFileName.isEmpty())
errorString = QLibrary::tr("The shared library was not found.");
}
@@ -522,7 +522,7 @@ void QLibraryPrivate::mergeLoadHints(QLibrary::LoadHints lh)
if (pHnd)
return;
- loadHintsInt.store(lh);
+ loadHintsInt.storeRelaxed(lh);
}
QFunctionPointer QLibraryPrivate::resolve(const char *symbol)
@@ -575,7 +575,7 @@ bool QLibraryPrivate::unload(UnloadFlag flag)
{
if (!pHnd)
return false;
- if (libraryUnloadCount.load() > 0 && !libraryUnloadCount.deref()) { // only unload if ALL QLibrary instance wanted to
+ if (libraryUnloadCount.loadRelaxed() > 0 && !libraryUnloadCount.deref()) { // only unload if ALL QLibrary instance wanted to
delete inst.data();
if (flag == NoUnloadSys || unload_sys()) {
if (qt_debug_component())
diff --git a/src/corelib/plugin/qlibrary_p.h b/src/corelib/plugin/qlibrary_p.h
index 1a216c98b5..db5afac98e 100644
--- a/src/corelib/plugin/qlibrary_p.h
+++ b/src/corelib/plugin/qlibrary_p.h
@@ -92,7 +92,7 @@ public:
QFunctionPointer resolve(const char *);
QLibrary::LoadHints loadHints() const
- { return QLibrary::LoadHints(loadHintsInt.load()); }
+ { return QLibrary::LoadHints(loadHintsInt.loadRelaxed()); }
void setLoadHints(QLibrary::LoadHints lh);
static QLibraryPrivate *findOrCreate(const QString &fileName, const QString &version = QString(),
diff --git a/src/corelib/plugin/qlibrary_unix.cpp b/src/corelib/plugin/qlibrary_unix.cpp
index e03814984c..84fcc53d52 100644
--- a/src/corelib/plugin/qlibrary_unix.cpp
+++ b/src/corelib/plugin/qlibrary_unix.cpp
@@ -77,14 +77,14 @@ QStringList QLibraryPrivate::suffixes_sys(const QString& fullVersion)
// .so is preferred.
# if defined(__ia64)
if (!fullVersion.isEmpty()) {
- suffixes << QString::fromLatin1(".so.%1").arg(fullVersion);
+ suffixes << QLatin1String(".so.%1").arg(fullVersion);
} else {
suffixes << QLatin1String(".so");
}
# endif
if (!fullVersion.isEmpty()) {
- suffixes << QString::fromLatin1(".sl.%1").arg(fullVersion);
- suffixes << QString::fromLatin1(".%1").arg(fullVersion);
+ suffixes << QLatin1String(".sl.%1").arg(fullVersion);
+ suffixes << QLatin1String(".%1").arg(fullVersion);
} else {
suffixes << QLatin1String(".sl");
}
@@ -93,15 +93,15 @@ QStringList QLibraryPrivate::suffixes_sys(const QString& fullVersion)
#else
if (!fullVersion.isEmpty()) {
- suffixes << QString::fromLatin1(".so.%1").arg(fullVersion);
+ suffixes << QLatin1String(".so.%1").arg(fullVersion);
} else {
suffixes << QLatin1String(".so");
}
#endif
# ifdef Q_OS_MAC
if (!fullVersion.isEmpty()) {
- suffixes << QString::fromLatin1(".%1.bundle").arg(fullVersion);
- suffixes << QString::fromLatin1(".%1.dylib").arg(fullVersion);
+ suffixes << QLatin1String(".%1.bundle").arg(fullVersion);
+ suffixes << QLatin1String(".%1.dylib").arg(fullVersion);
} else {
suffixes << QLatin1String(".bundle") << QLatin1String(".dylib");
}
diff --git a/src/corelib/serialization/qcborvalue.cpp b/src/corelib/serialization/qcborvalue.cpp
index b2e0ba6d53..ba616c0a7d 100644
--- a/src/corelib/serialization/qcborvalue.cpp
+++ b/src/corelib/serialization/qcborvalue.cpp
@@ -849,7 +849,7 @@ QCborContainerPrivate *QCborContainerPrivate::clone(QCborContainerPrivate *d, qs
QCborContainerPrivate *QCborContainerPrivate::detach(QCborContainerPrivate *d, qsizetype reserved)
{
- if (!d || d->ref.load() != 1)
+ if (!d || d->ref.loadRelaxed() != 1)
return clone(d, reserved);
return d;
}
@@ -884,12 +884,12 @@ void QCborContainerPrivate::replaceAt_complex(Element &e, const QCborValue &valu
// detect self-assignment
if (Q_UNLIKELY(this == value.container)) {
- Q_ASSERT(ref.load() >= 2);
+ Q_ASSERT(ref.loadRelaxed() >= 2);
if (disp == MoveContainer)
ref.deref(); // not deref() because it can't drop to 0
QCborContainerPrivate *d = QCborContainerPrivate::clone(this);
d->elements.detach();
- d->ref.store(1);
+ d->ref.storeRelaxed(1);
e.container = d;
} else {
e.container = value.container;
@@ -1368,7 +1368,7 @@ static Element decodeBasicValueFromCbor(QCborStreamReader &reader)
static inline QCborContainerPrivate *createContainerFromCbor(QCborStreamReader &reader)
{
auto d = new QCborContainerPrivate;
- d->ref.store(1);
+ d->ref.storeRelaxed(1);
d->decodeFromCbor(reader);
return d;
}
@@ -1643,7 +1643,7 @@ QCborValue::QCborValue(const QByteArray &ba)
: n(0), container(new QCborContainerPrivate), t(ByteArray)
{
container->appendByteData(ba.constData(), ba.size(), t);
- container->ref.store(1);
+ container->ref.storeRelaxed(1);
}
/*!
@@ -1656,7 +1656,7 @@ QCborValue::QCborValue(const QString &s)
: n(0), container(new QCborContainerPrivate), t(String)
{
container->append(s);
- container->ref.store(1);
+ container->ref.storeRelaxed(1);
}
/*!
@@ -1671,7 +1671,7 @@ QCborValue::QCborValue(QLatin1String s)
: n(0), container(new QCborContainerPrivate), t(String)
{
container->append(s);
- container->ref.store(1);
+ container->ref.storeRelaxed(1);
}
/*!
@@ -1719,7 +1719,7 @@ QCborValue::QCborValue(const QCborMap &m)
QCborValue::QCborValue(QCborTag t, const QCborValue &tv)
: n(-1), container(new QCborContainerPrivate), t(Tag)
{
- container->ref.store(1);
+ container->ref.storeRelaxed(1);
container->append(t);
container->append(tv);
}
diff --git a/src/corelib/serialization/qdatastream.cpp b/src/corelib/serialization/qdatastream.cpp
index 2c29b81f7c..b3330d6cea 100644
--- a/src/corelib/serialization/qdatastream.cpp
+++ b/src/corelib/serialization/qdatastream.cpp
@@ -277,7 +277,7 @@ QT_BEGIN_NAMESPACE
QDataStream::QDataStream()
{
- dev = 0;
+ dev = nullptr;
owndev = false;
byteorder = BigEndian;
ver = Qt_DefaultCompiledVersion;
@@ -433,7 +433,7 @@ bool QDataStream::atEnd() const
*/
QDataStream::FloatingPointPrecision QDataStream::floatingPointPrecision() const
{
- return d == 0 ? QDataStream::DoublePrecision : d->floatingPointPrecision;
+ return d ? d->floatingPointPrecision : QDataStream::DoublePrecision;
}
/*!
@@ -458,7 +458,7 @@ QDataStream::FloatingPointPrecision QDataStream::floatingPointPrecision() const
*/
void QDataStream::setFloatingPointPrecision(QDataStream::FloatingPointPrecision precision)
{
- if (d == 0)
+ if (!d)
d.reset(new QDataStreamPrivate());
d->floatingPointPrecision = precision;
}
@@ -639,7 +639,7 @@ void QDataStream::startTransaction()
{
CHECK_STREAM_PRECOND(Q_VOID)
- if (d == 0)
+ if (!d)
d.reset(new QDataStreamPrivate());
if (++d->transactionDepth == 1) {
@@ -1043,7 +1043,7 @@ QDataStream &QDataStream::operator>>(char *&s)
QDataStream &QDataStream::readBytes(char *&s, uint &l)
{
- s = 0;
+ s = nullptr;
l = 0;
CHECK_STREAM_PRECOND(*this)
@@ -1054,8 +1054,8 @@ QDataStream &QDataStream::readBytes(char *&s, uint &l)
const quint32 Step = 1024 * 1024;
quint32 allocated = 0;
- char *prevBuf = 0;
- char *curBuf = 0;
+ char *prevBuf = nullptr;
+ char *curBuf = nullptr;
do {
int blockSize = qMin(Step, len - allocated);
diff --git a/src/corelib/serialization/qjson_p.h b/src/corelib/serialization/qjson_p.h
index 85fb2a1254..9de53d5b74 100644
--- a/src/corelib/serialization/qjson_p.h
+++ b/src/corelib/serialization/qjson_p.h
@@ -719,7 +719,7 @@ public:
Data *clone(Base *b, int reserve = 0)
{
int size = sizeof(Header) + b->size;
- if (b == header->root() && ref.load() == 1 && alloc >= size + reserve)
+ if (b == header->root() && ref.loadRelaxed() == 1 && alloc >= size + reserve)
return this;
if (reserve) {
diff --git a/src/corelib/serialization/qjsonarray.cpp b/src/corelib/serialization/qjsonarray.cpp
index 7dfa9b43f0..6b327619ad 100644
--- a/src/corelib/serialization/qjsonarray.cpp
+++ b/src/corelib/serialization/qjsonarray.cpp
@@ -132,7 +132,7 @@ QT_BEGIN_NAMESPACE
Creates an empty array.
*/
QJsonArray::QJsonArray()
- : d(0), a(0)
+ : d(nullptr), a(nullptr)
{
}
@@ -168,8 +168,8 @@ QJsonArray::QJsonArray(QJsonPrivate::Data *data, QJsonPrivate::Array *array)
*/
void QJsonArray::initialize()
{
- d = 0;
- a = 0;
+ d = nullptr;
+ a = nullptr;
}
/*!
@@ -1226,7 +1226,7 @@ bool QJsonArray::detach2(uint reserve)
d->ref.ref();
return true;
}
- if (reserve == 0 && d->ref.load() == 1)
+ if (reserve == 0 && d->ref.loadRelaxed() == 1)
return true;
QJsonPrivate::Data *x = d->clone(a, reserve);
diff --git a/src/corelib/serialization/qjsoncbor.cpp b/src/corelib/serialization/qjsoncbor.cpp
index dc5f384108..e0c390f610 100644
--- a/src/corelib/serialization/qjsoncbor.cpp
+++ b/src/corelib/serialization/qjsoncbor.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2018 Intel Corporation.
+** Copyright (C) 2019 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -598,10 +598,12 @@ QCborValue QCborValue::fromJsonValue(const QJsonValue &v)
switch (v.type()) {
case QJsonValue::Bool:
return v.b;
- case QJsonValue::Double:
- if (v.dbl == qint64(v.dbl))
- return qint64(v.dbl);
+ case QJsonValue::Double: {
+ qint64 i;
+ if (convertDoubleTo(v.dbl, &i))
+ return i;
return v.dbl;
+ }
case QJsonValue::String:
return v.toString();
case QJsonValue::Array:
diff --git a/src/corelib/serialization/qjsondocument.cpp b/src/corelib/serialization/qjsondocument.cpp
index 179a87c699..f8027efb58 100644
--- a/src/corelib/serialization/qjsondocument.cpp
+++ b/src/corelib/serialization/qjsondocument.cpp
@@ -84,7 +84,7 @@ QT_BEGIN_NAMESPACE
* Constructs an empty and invalid document.
*/
QJsonDocument::QJsonDocument()
- : d(0)
+ : d(nullptr)
{
}
@@ -92,7 +92,7 @@ QJsonDocument::QJsonDocument()
* Creates a QJsonDocument from \a object.
*/
QJsonDocument::QJsonDocument(const QJsonObject &object)
- : d(0)
+ : d(nullptr)
{
setObject(object);
}
@@ -101,7 +101,7 @@ QJsonDocument::QJsonDocument(const QJsonObject &object)
* Constructs a QJsonDocument from \a array.
*/
QJsonDocument::QJsonDocument(const QJsonArray &array)
- : d(0)
+ : d(nullptr)
{
setArray(array);
}
@@ -236,7 +236,7 @@ const char *QJsonDocument::rawData(int *size) const
{
if (!d) {
*size = 0;
- return 0;
+ return nullptr;
}
*size = d->alloc;
return d->rawData;
@@ -635,7 +635,7 @@ bool QJsonDocument::operator==(const QJsonDocument &other) const
*/
bool QJsonDocument::isNull() const
{
- return (d == 0);
+ return (d == nullptr);
}
#if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY)
diff --git a/src/corelib/serialization/qjsonobject.cpp b/src/corelib/serialization/qjsonobject.cpp
index a9f25a119c..c1a6328b2f 100644
--- a/src/corelib/serialization/qjsonobject.cpp
+++ b/src/corelib/serialization/qjsonobject.cpp
@@ -110,7 +110,7 @@ QT_BEGIN_NAMESPACE
\sa isEmpty()
*/
QJsonObject::QJsonObject()
- : d(0), o(0)
+ : d(nullptr), o(nullptr)
{
}
@@ -149,8 +149,8 @@ QJsonObject::QJsonObject(QJsonPrivate::Data *data, QJsonPrivate::Object *object)
void QJsonObject::initialize()
{
- d = 0;
- o = 0;
+ d = nullptr;
+ o = nullptr;
}
/*!
@@ -646,7 +646,7 @@ bool QJsonObject::operator!=(const QJsonObject &other) const
*/
QJsonObject::iterator QJsonObject::erase(QJsonObject::iterator it)
{
- Q_ASSERT(d && d->ref.load() == 1);
+ Q_ASSERT(d && d->ref.loadRelaxed() == 1);
if (it.o != this || it.i < 0 || it.i >= (int)o->length)
return iterator(this, o->length);
@@ -1231,7 +1231,7 @@ bool QJsonObject::detach2(uint reserve)
d->ref.ref();
return true;
}
- if (reserve == 0 && d->ref.load() == 1)
+ if (reserve == 0 && d->ref.loadRelaxed() == 1)
return true;
QJsonPrivate::Data *x = d->clone(o, reserve);
diff --git a/src/corelib/serialization/qjsonparser.cpp b/src/corelib/serialization/qjsonparser.cpp
index f29348d593..cd36bd5a5b 100644
--- a/src/corelib/serialization/qjsonparser.cpp
+++ b/src/corelib/serialization/qjsonparser.cpp
@@ -198,7 +198,9 @@ QString QJsonParseError::errorString() const
using namespace QJsonPrivate;
Parser::Parser(const char *json, int length)
- : head(json), json(json), data(0), dataLength(0), current(0), nestingLevel(0), lastError(QJsonParseError::NoError)
+ : head(json), json(json), data(nullptr)
+ , dataLength(0), current(0), nestingLevel(0)
+ , lastError(QJsonParseError::NoError)
{
end = json + length;
}
diff --git a/src/corelib/serialization/qjsonvalue.cpp b/src/corelib/serialization/qjsonvalue.cpp
index 1fc610d7c7..0bd28581f3 100644
--- a/src/corelib/serialization/qjsonvalue.cpp
+++ b/src/corelib/serialization/qjsonvalue.cpp
@@ -112,7 +112,7 @@ QT_BEGIN_NAMESPACE
The default is to create a Null value.
*/
QJsonValue::QJsonValue(Type type)
- : ui(0), d(0), t(type)
+ : ui(0), d(nullptr), t(type)
{
}
@@ -120,7 +120,7 @@ QJsonValue::QJsonValue(Type type)
\internal
*/
QJsonValue::QJsonValue(QJsonPrivate::Data *data, QJsonPrivate::Base *base, const QJsonPrivate::Value &v)
- : d(0)
+ : d(nullptr)
{
t = (Type)(uint)v.type;
switch (t) {
@@ -154,7 +154,7 @@ QJsonValue::QJsonValue(QJsonPrivate::Data *data, QJsonPrivate::Base *base, const
Creates a value of type Bool, with value \a b.
*/
QJsonValue::QJsonValue(bool b)
- : d(0), t(Bool)
+ : d(nullptr), t(Bool)
{
this->b = b;
}
@@ -163,7 +163,7 @@ QJsonValue::QJsonValue(bool b)
Creates a value of type Double, with value \a n.
*/
QJsonValue::QJsonValue(double n)
- : d(0), t(Double)
+ : d(nullptr), t(Double)
{
this->dbl = n;
}
@@ -173,7 +173,7 @@ QJsonValue::QJsonValue(double n)
Creates a value of type Double, with value \a n.
*/
QJsonValue::QJsonValue(int n)
- : d(0), t(Double)
+ : d(nullptr), t(Double)
{
this->dbl = n;
}
@@ -185,7 +185,7 @@ QJsonValue::QJsonValue(int n)
If you pass in values outside this range expect a loss of precision to occur.
*/
QJsonValue::QJsonValue(qint64 n)
- : d(0), t(Double)
+ : d(nullptr), t(Double)
{
this->dbl = double(n);
}
@@ -194,7 +194,7 @@ QJsonValue::QJsonValue(qint64 n)
Creates a value of type String, with value \a s.
*/
QJsonValue::QJsonValue(const QString &s)
- : d(0), t(String)
+ : d(nullptr), t(String)
{
stringDataFromQStringHelper(s);
}
@@ -221,7 +221,7 @@ void QJsonValue::stringDataFromQStringHelper(const QString &string)
Creates a value of type String, with value \a s.
*/
QJsonValue::QJsonValue(QLatin1String s)
- : d(0), t(String)
+ : d(nullptr), t(String)
{
// ### FIXME: Avoid creating the temp QString below
QString str(s);
diff --git a/src/corelib/serialization/qtextstream.cpp b/src/corelib/serialization/qtextstream.cpp
index ef2a9c97ee..9d4bc223ab 100644
--- a/src/corelib/serialization/qtextstream.cpp
+++ b/src/corelib/serialization/qtextstream.cpp
@@ -327,7 +327,7 @@ QT_BEGIN_NAMESPACE
QTextStreamPrivate::QTextStreamPrivate(QTextStream *q_ptr)
:
#if QT_CONFIG(textcodec)
- readConverterSavedState(0),
+ readConverterSavedState(nullptr),
#endif
readConverterSavedStateOffset(0),
locale(QLocale::c())
@@ -381,7 +381,7 @@ void QTextStreamPrivate::Params::reset()
padChar = QLatin1Char(' ');
fieldAlignment = QTextStream::AlignRight;
realNumberNotation = QTextStream::SmartNotation;
- numberFlags = 0;
+ numberFlags = { };
}
/*!
@@ -391,9 +391,9 @@ void QTextStreamPrivate::reset()
{
params.reset();
- device = 0;
+ device = nullptr;
deleteDevice = false;
- string = 0;
+ string = nullptr;
stringOffset = 0;
stringOpenMode = QIODevice::NotOpen;
@@ -406,7 +406,7 @@ void QTextStreamPrivate::reset()
resetCodecConverterStateHelper(&readConverterState);
resetCodecConverterStateHelper(&writeConverterState);
delete readConverterSavedState;
- readConverterSavedState = 0;
+ readConverterSavedState = nullptr;
writeConverterState.flags |= QTextCodec::IgnoreHeader;
autoDetectUnicode = true;
#endif
@@ -1207,7 +1207,7 @@ bool QTextStream::seek(qint64 pos)
resetCodecConverterStateHelper(&d->readConverterState);
resetCodecConverterStateHelper(&d->writeConverterState);
delete d->readConverterSavedState;
- d->readConverterSavedState = 0;
+ d->readConverterSavedState = nullptr;
d->writeConverterState.flags |= QTextCodec::IgnoreHeader;
#endif
return true;
@@ -1295,7 +1295,7 @@ void QTextStream::skipWhiteSpace()
{
Q_D(QTextStream);
CHECK_VALID_STREAM(Q_VOID);
- d->scan(0, 0, 0, QTextStreamPrivate::NotSpace);
+ d->scan(nullptr, nullptr, 0, QTextStreamPrivate::NotSpace);
d->consumeLastToken();
}
@@ -1751,7 +1751,7 @@ QString QTextStream::read(qint64 maxlen)
*/
QTextStreamPrivate::NumberParsingStatus QTextStreamPrivate::getNumber(qulonglong *ret)
{
- scan(0, 0, 0, NotSpace);
+ scan(nullptr, nullptr, 0, NotSpace);
consumeLastToken();
// detect int encoding
@@ -1980,7 +1980,7 @@ bool QTextStreamPrivate::getReal(double *f)
ParserState state = Init;
InputToken input = None;
- scan(0, 0, 0, NotSpace);
+ scan(nullptr, nullptr, 0, NotSpace);
consumeLastToken();
const int BufferSize = 128;
@@ -2084,7 +2084,7 @@ QTextStream &QTextStream::operator>>(QChar &c)
{
Q_D(QTextStream);
CHECK_VALID_STREAM(*this);
- d->scan(0, 0, 0, QTextStreamPrivate::NotSpace);
+ d->scan(nullptr, nullptr, 0, QTextStreamPrivate::NotSpace);
if (!d->getChar(&c))
setStatus(ReadPastEnd);
return *this;
@@ -2245,7 +2245,7 @@ QTextStream &QTextStream::operator>>(QString &str)
CHECK_VALID_STREAM(*this);
str.clear();
- d->scan(0, 0, 0, QTextStreamPrivate::NotSpace);
+ d->scan(nullptr, nullptr, 0, QTextStreamPrivate::NotSpace);
d->consumeLastToken();
const QChar *ptr;
@@ -2273,7 +2273,7 @@ QTextStream &QTextStream::operator>>(QByteArray &array)
CHECK_VALID_STREAM(*this);
array.clear();
- d->scan(0, 0, 0, QTextStreamPrivate::NotSpace);
+ d->scan(nullptr, nullptr, 0, QTextStreamPrivate::NotSpace);
d->consumeLastToken();
const QChar *ptr;
@@ -2308,7 +2308,7 @@ QTextStream &QTextStream::operator>>(char *c)
Q_D(QTextStream);
*c = 0;
CHECK_VALID_STREAM(*this);
- d->scan(0, 0, 0, QTextStreamPrivate::NotSpace);
+ d->scan(nullptr, nullptr, 0, QTextStreamPrivate::NotSpace);
d->consumeLastToken();
const QChar *ptr;
diff --git a/src/corelib/serialization/qxmlstream.cpp b/src/corelib/serialization/qxmlstream.cpp
index d43d9c4e14..be3a476cb2 100644
--- a/src/corelib/serialization/qxmlstream.cpp
+++ b/src/corelib/serialization/qxmlstream.cpp
@@ -58,9 +58,9 @@
// case for most bootstrapped applications.
#define Q_DECLARE_TR_FUNCTIONS(context) \
public: \
- static inline QString tr(const char *sourceText, const char *comment = 0) \
+ static inline QString tr(const char *sourceText, const char *comment = nullptr) \
{ Q_UNUSED(comment); return QString::fromLatin1(sourceText); } \
- static inline QString trUtf8(const char *sourceText, const char *comment = 0) \
+ static inline QString trUtf8(const char *sourceText, const char *comment = nullptr) \
{ Q_UNUSED(comment); return QString::fromLatin1(sourceText); } \
static inline QString tr(const char *sourceText, const char*, int) \
{ return QString::fromLatin1(sourceText); } \
@@ -548,7 +548,7 @@ void QXmlStreamReader::clear()
if (d->device) {
if (d->deleteDevice)
delete d->device;
- d->device = 0;
+ d->device = nullptr;
}
}
@@ -792,16 +792,16 @@ QXmlStreamPrivateTagStack::QXmlStreamPrivateTagStack()
QXmlStreamReaderPrivate::QXmlStreamReaderPrivate(QXmlStreamReader *q)
:q_ptr(q)
{
- device = 0;
+ device = nullptr;
deleteDevice = false;
#if QT_CONFIG(textcodec)
- decoder = 0;
+ decoder = nullptr;
#endif
stack_size = 64;
- sym_stack = 0;
- state_stack = 0;
+ sym_stack = nullptr;
+ state_stack = nullptr;
reallocateStack();
- entityResolver = 0;
+ entityResolver = nullptr;
init();
#define ADD_PREDEFINED(n, v) \
do { \
@@ -843,11 +843,11 @@ void QXmlStreamReaderPrivate::init()
#if QT_CONFIG(textcodec)
codec = QTextCodec::codecForMib(106); // utf8
delete decoder;
- decoder = 0;
+ decoder = nullptr;
#endif
attributeStack.clear();
attributeStack.reserve(16);
- entityParser = 0;
+ entityParser = nullptr;
hasCheckedStartDocument = false;
normalizeLiterals = false;
hasSeenTag = false;
@@ -3024,8 +3024,8 @@ QXmlStreamWriterPrivate::QXmlStreamWriterPrivate(QXmlStreamWriter *q)
:autoFormattingIndent(4, ' ')
{
q_ptr = q;
- device = 0;
- stringDevice = 0;
+ device = nullptr;
+ stringDevice = nullptr;
deleteDevice = false;
#if QT_CONFIG(textcodec)
codec = QTextCodec::codecForMib(106); // utf8
@@ -3315,7 +3315,7 @@ void QXmlStreamWriter::setDevice(QIODevice *device)
Q_D(QXmlStreamWriter);
if (device == d->device)
return;
- d->stringDevice = 0;
+ d->stringDevice = nullptr;
if (d->deleteDevice) {
delete d->device;
d->deleteDevice = false;
diff --git a/src/corelib/serialization/qxmlstream.g b/src/corelib/serialization/qxmlstream.g
index e6328a11ac..12ecc9bdb2 100644
--- a/src/corelib/serialization/qxmlstream.g
+++ b/src/corelib/serialization/qxmlstream.g
@@ -506,7 +506,7 @@ public:
int fastScanLiteralContent();
int fastScanSpace();
int fastScanContentCharList();
- int fastScanName(int *prefix = 0);
+ int fastScanName(int *prefix = nullptr);
inline int fastScanNMTOKEN();
diff --git a/src/corelib/thread/qatomic.cpp b/src/corelib/thread/qatomic.cpp
index c161bec537..b1a7edad91 100644
--- a/src/corelib/thread/qatomic.cpp
+++ b/src/corelib/thread/qatomic.cpp
@@ -258,12 +258,26 @@
/*!
\fn template <typename T> T QAtomicInteger<T>::load() const
+ \obsolete
+
+ Use loadRelaxed() instead.
Atomically loads the value of this QAtomicInteger using relaxed memory
ordering. The value is not modified in any way, but note that there's no
guarantee that it remains so.
- \sa store(), loadAcquire()
+ \sa storeRelaxed(), loadAcquire()
+*/
+
+/*!
+ \fn template <typename T> T QAtomicInteger<T>::loadRelaxed() const
+ \since 5.14
+
+ Atomically loads the value of this QAtomicInteger using relaxed memory
+ ordering. The value is not modified in any way, but note that there's no
+ guarantee that it remains so.
+
+ \sa storeRelaxed(), loadAcquire()
*/
/*!
@@ -273,16 +287,29 @@
ordering. The value is not modified in any way, but note that there's no
guarantee that it remains so.
- \sa store(), load()
+ \sa storeRelaxed(), loadRelaxed()
*/
/*!
\fn template <typename T> void QAtomicInteger<T>::store(T newValue)
+ \obsolete
+
+ Use storeRelaxed() instead.
Atomically stores the \a newValue value into this atomic type, using
relaxed memory ordering.
- \sa storeRelease(), load()
+ \sa storeRelease(), loadRelaxed()
+*/
+
+/*!
+ \fn template <typename T> void QAtomicInteger<T>::storeRelaxed(T newValue)
+ \since 5.14
+
+ Atomically stores the \a newValue value into this atomic type, using
+ relaxed memory ordering.
+
+ \sa storeRelease(), loadRelaxed()
*/
/*!
@@ -291,7 +318,7 @@
Atomically stores the \a newValue value into this atomic type, using
the "Release" memory ordering.
- \sa store(), load()
+ \sa store(), loadAcquire()
*/
/*!
@@ -303,7 +330,7 @@
value is not modified in any way, but note that there's no guarantee that
it remains so.
- \sa load(), loadAcquire()
+ \sa loadRelaxed(), loadAcquire()
*/
/*!
@@ -314,7 +341,7 @@
sequentially consistent memory ordering if possible; or "Release" ordering
if not. This function returns a reference to this object.
- \sa store(), storeRelease()
+ \sa storeRelaxed(), storeRelease()
*/
/*! \fn template <typename T> bool QAtomicInteger<T>::isReferenceCountingNative()
@@ -1278,31 +1305,59 @@
/*!
\fn template <typename T> T *QAtomicPointer<T>::load() const
+ \obsolete
+
+ Use loadRelaxed() instead.
Atomically loads the value of this QAtomicPointer using relaxed memory
ordering. The value is not modified in any way, but note that there's no
guarantee that it remains so.
- \sa store(), loadAcquire()
+ \sa storeRelaxed(), loadAcquire()
*/
/*!
+ \fn template <typename T> T *QAtomicPointer<T>::loadRelaxed() const
+ \since 5.14
+
+ Atomically loads the value of this QAtomicPointer using relaxed memory
+ ordering. The value is not modified in any way, but note that there's no
+ guarantee that it remains so.
+
+ \sa storeRelaxed(), loadAcquire()
+*/
+
+
+/*!
\fn template <typename T> T *QAtomicPointer<T>::loadAcquire() const
Atomically loads the value of this QAtomicPointer using the "Acquire" memory
ordering. The value is not modified in any way, but note that there's no
guarantee that it remains so.
- \sa store(), load()
+ \sa storeRelease(), loadRelaxed()
*/
/*!
\fn template <typename T> void QAtomicPointer<T>::store(T *newValue)
+ \obsolete
+
+ Use storeRelaxed() instead.
+
+ Atomically stores the \a newValue value into this atomic type, using
+ relaxed memory ordering.
+
+ \sa storeRelease(), loadRelaxed()
+*/
+
+/*!
+ \fn template <typename T> void QAtomicPointer<T>::storeRelaxed(T *newValue)
+ \since 5.14
Atomically stores the \a newValue value into this atomic type, using
relaxed memory ordering.
- \sa storeRelease(), load()
+ \sa storeRelease(), loadRelaxed()
*/
/*!
@@ -1311,7 +1366,7 @@
Atomically stores the \a newValue value into this atomic type, using
the "Release" memory ordering.
- \sa store(), load()
+ \sa storeRelaxed(), loadRelaxed()
*/
/*! \fn template <typename T> bool QAtomicPointer<T>::isTestAndSetNative()
diff --git a/src/corelib/thread/qatomic.h b/src/corelib/thread/qatomic.h
index 280ce96b76..a3b9be0729 100644
--- a/src/corelib/thread/qatomic.h
+++ b/src/corelib/thread/qatomic.h
@@ -81,8 +81,10 @@ public:
#ifdef Q_CLANG_QDOC
T load() const;
+ T loadRelaxed() const;
T loadAcquire() const;
void store(T newValue);
+ void storeRelaxed(T newValue);
void storeRelease(T newValue);
operator T() const;
@@ -172,7 +174,7 @@ public:
#else
inline QAtomicPointer(T *value = nullptr) noexcept
{
- this->store(value);
+ this->storeRelaxed(value);
}
#endif
inline QAtomicPointer(const QAtomicPointer<T> &other) noexcept
@@ -255,7 +257,7 @@ inline void qAtomicAssign(T *&d, T *x)
template <typename T>
inline void qAtomicDetach(T *&d)
{
- if (d->ref.load() == 1)
+ if (d->ref.loadRelaxed() == 1)
return;
T *x = d;
d = new T(*d);
diff --git a/src/corelib/thread/qatomic_cxx11.h b/src/corelib/thread/qatomic_cxx11.h
index 2851bae73e..7386aee126 100644
--- a/src/corelib/thread/qatomic_cxx11.h
+++ b/src/corelib/thread/qatomic_cxx11.h
@@ -234,6 +234,18 @@ template <typename X> struct QAtomicOps
}
template <typename T> static inline
+ T loadRelaxed(const std::atomic<T> &_q_value) noexcept
+ {
+ return _q_value.load(std::memory_order_relaxed);
+ }
+
+ template <typename T> static inline
+ T loadRelaxed(const volatile std::atomic<T> &_q_value) noexcept
+ {
+ return _q_value.load(std::memory_order_relaxed);
+ }
+
+ template <typename T> static inline
T loadAcquire(const std::atomic<T> &_q_value) noexcept
{
return _q_value.load(std::memory_order_acquire);
@@ -252,6 +264,12 @@ template <typename X> struct QAtomicOps
}
template <typename T> static inline
+ void storeRelaxed(std::atomic<T> &_q_value, T newValue) noexcept
+ {
+ _q_value.store(newValue, std::memory_order_relaxed);
+ }
+
+ template <typename T> static inline
void storeRelease(std::atomic<T> &_q_value, T newValue) noexcept
{
_q_value.store(newValue, std::memory_order_release);
diff --git a/src/corelib/thread/qbasicatomic.h b/src/corelib/thread/qbasicatomic.h
index 7d2e06a499..9804e60119 100644
--- a/src/corelib/thread/qbasicatomic.h
+++ b/src/corelib/thread/qbasicatomic.h
@@ -99,9 +99,13 @@ public:
typename Ops::Type _q_value;
// Everything below is either implemented in ../arch/qatomic_XXX.h or (as fallback) in qgenericatomic.h
+#if QT_DEPRECATED_SINCE(5, 14)
+ QT_DEPRECATED_VERSION_X_5_14("Use loadRelaxed") T load() const noexcept { return loadRelaxed(); }
+ QT_DEPRECATED_VERSION_X_5_14("Use storeRelaxed") void store(T newValue) noexcept { storeRelaxed(newValue); }
+#endif
- T load() const noexcept { return Ops::load(_q_value); }
- void store(T newValue) noexcept { Ops::store(_q_value, newValue); }
+ T loadRelaxed() const noexcept { return Ops::loadRelaxed(_q_value); }
+ void storeRelaxed(T newValue) noexcept { Ops::storeRelaxed(_q_value, newValue); }
T loadAcquire() const noexcept { return Ops::loadAcquire(_q_value); }
void storeRelease(T newValue) noexcept { Ops::storeRelease(_q_value, newValue); }
@@ -236,8 +240,14 @@ public:
AtomicType _q_value;
- Type load() const noexcept { return Ops::load(_q_value); }
- void store(Type newValue) noexcept { Ops::store(_q_value, newValue); }
+#if QT_DEPRECATED_SINCE(5, 14)
+ QT_DEPRECATED_VERSION_X_5_14("Use loadRelaxed") Type load() const noexcept { return loadRelaxed(); }
+ QT_DEPRECATED_VERSION_X_5_14("Use storeRelaxed") void store(Type newValue) noexcept { storeRelaxed(newValue); }
+#endif
+
+ Type loadRelaxed() const noexcept { return Ops::loadRelaxed(_q_value); }
+ void storeRelaxed(Type newValue) noexcept { Ops::storeRelaxed(_q_value, newValue); }
+
operator Type() const noexcept { return loadAcquire(); }
Type operator=(Type newValue) noexcept { storeRelease(newValue); return newValue; }
diff --git a/src/corelib/thread/qfuture.h b/src/corelib/thread/qfuture.h
index a456dd9139..d3135510b3 100644
--- a/src/corelib/thread/qfuture.h
+++ b/src/corelib/thread/qfuture.h
@@ -111,33 +111,79 @@ public:
typedef const T &reference;
inline const_iterator() {}
- inline const_iterator(QFuture const * const _future, int _index) : future(_future), index(_index) {}
+ inline const_iterator(QFuture const * const _future, int _index)
+ : future(_future), index(advanceIndex(_index, 0)) { }
inline const_iterator(const const_iterator &o) : future(o.future), index(o.index) {}
inline const_iterator &operator=(const const_iterator &o)
{ future = o.future; index = o.index; return *this; }
inline const T &operator*() const { return future->d.resultReference(index); }
inline const T *operator->() const { return future->d.resultPointer(index); }
-
- inline bool operator!=(const const_iterator &other) const
+ inline bool operator!=(const const_iterator &other) const { return index != other.index; }
+ inline bool operator==(const const_iterator &o) const { return !operator!=(o); }
+ inline const_iterator &operator++()
+ { index = advanceIndex(index, 1); return *this; }
+ inline const_iterator &operator--()
+ { index = advanceIndex(index, -1); return *this; }
+ inline const_iterator operator++(int)
+ {
+ const_iterator r = *this;
+ index = advanceIndex(index, 1);
+ return r;
+ }
+ inline const_iterator operator--(int)
{
- if (index == -1 && other.index == -1) // comparing end != end?
- return false;
- if (other.index == -1)
- return (future->isRunning() || (index < future->resultCount()));
- return (index != other.index);
+ const_iterator r = *this;
+ index = advanceIndex(index, -1);
+ return r;
}
+ inline const_iterator operator+(int j) const
+ { return const_iterator(future, advanceIndex(index, j)); }
+ inline const_iterator operator-(int j) const
+ { return const_iterator(future, advanceIndex(index, -j)); }
+ inline const_iterator &operator+=(int j)
+ { index = advanceIndex(index, j); return *this; }
+ inline const_iterator &operator-=(int j)
+ { index = advanceIndex(index, -j); return *this; }
+ friend inline const_iterator operator+(int j, const_iterator k)
+ { return const_iterator(k.future, k.advanceIndex(k.index, j)); }
- inline bool operator==(const const_iterator &o) const { return !operator!=(o); }
- inline const_iterator &operator++() { ++index; return *this; }
- inline const_iterator operator++(int) { const_iterator r = *this; ++index; return r; }
- inline const_iterator &operator--() { --index; return *this; }
- inline const_iterator operator--(int) { const_iterator r = *this; --index; return r; }
- inline const_iterator operator+(int j) const { return const_iterator(future, index + j); }
- inline const_iterator operator-(int j) const { return const_iterator(future, index - j); }
- inline const_iterator &operator+=(int j) { index += j; return *this; }
- inline const_iterator &operator-=(int j) { index -= j; return *this; }
- friend inline const_iterator operator+(int j, const_iterator k) { return k + j; }
private:
+ /*! \internal
+
+ Advances the iterator index \a idx \a n steps, waits for the
+ result at the target index, and returns the target index.
+
+ The index may be -1, indicating the end iterator, either
+ as the argument or as the return value. The end iterator
+ may be decremented.
+
+ The caller is responsible for not advancing the iterator
+ before begin() or past end(), with the exception that
+ attempting to advance a non-end iterator past end() for
+ a running future is allowed and will return the end iterator.
+
+ Note that n == 0 is valid and will wait for the result
+ at the given index.
+ */
+ int advanceIndex(int idx, int n) const
+ {
+ // The end iterator can be decremented, leave as-is for other cases
+ if (idx == -1 && n >= 0)
+ return idx;
+
+ // Special case for decrementing the end iterator: wait for
+ // finished to get the total result count.
+ if (idx == -1 && future->isRunning())
+ future->d.waitForFinished();
+
+ // Wait for result at target index
+ const int targetIndex = (idx == -1) ? future->resultCount() + n : idx + n;
+ future->d.waitForResult(targetIndex);
+
+ // After waiting there is either a result or the end was reached
+ return (targetIndex < future->resultCount()) ? targetIndex : -1;
+ }
+
QFuture const * future;
int index;
};
diff --git a/src/corelib/thread/qfuture.qdoc b/src/corelib/thread/qfuture.qdoc
index 7db65dacd3..076725e19c 100644
--- a/src/corelib/thread/qfuture.qdoc
+++ b/src/corelib/thread/qfuture.qdoc
@@ -55,8 +55,8 @@
instance, the computation can be canceled with the cancel() function. To
pause the computation, use the setPaused() function or one of the pause(),
resume(), or togglePaused() convenience functions. Be aware that not all
- asynchronous computations can be canceled or paused. For example, the
- future returned by QtConcurrent::run() cannot be canceled; but the
+ running asynchronous computations can be canceled or paused. For example,
+ the future returned by QtConcurrent::run() cannot be canceled; but the
future returned by QtConcurrent::mappedReduced() can.
Progress information is provided by the progressValue(),
@@ -133,8 +133,8 @@
Any QFutureWatcher object that is watching this future will not deliver
progress and result ready signals on a canceled future.
- Be aware that not all asynchronous computations can be canceled. For
- example, the future returned by QtConcurrent::run() cannot be canceled;
+ Be aware that not all running asynchronous computations can be canceled.
+ For example, the future returned by QtConcurrent::run() cannot be canceled;
but the future returned by QtConcurrent::mappedReduced() can.
*/
diff --git a/src/corelib/thread/qfutureinterface.cpp b/src/corelib/thread/qfutureinterface.cpp
index dfbed5fb0f..8f4cae8816 100644
--- a/src/corelib/thread/qfutureinterface.cpp
+++ b/src/corelib/thread/qfutureinterface.cpp
@@ -97,7 +97,7 @@ static inline int switch_off(QAtomicInt &a, int which)
static inline int switch_from_to(QAtomicInt &a, int from, int to)
{
int newValue;
- int expected = a.load();
+ int expected = a.loadRelaxed();
do {
newValue = (expected & ~from) | to;
} while (!a.testAndSetRelaxed(expected, newValue, expected));
@@ -107,7 +107,7 @@ static inline int switch_from_to(QAtomicInt &a, int from, int to)
void QFutureInterfaceBase::cancel()
{
QMutexLocker locker(&d->m_mutex);
- if (d->state.load() & Canceled)
+ if (d->state.loadRelaxed() & Canceled)
return;
switch_from_to(d->state, Paused, Canceled);
@@ -132,7 +132,7 @@ void QFutureInterfaceBase::setPaused(bool paused)
void QFutureInterfaceBase::togglePaused()
{
QMutexLocker locker(&d->m_mutex);
- if (d->state.load() & Paused) {
+ if (d->state.loadRelaxed() & Paused) {
switch_off(d->state, Paused);
d->pausedWaitCondition.wakeAll();
d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::Resumed));
@@ -149,7 +149,7 @@ void QFutureInterfaceBase::setThrottled(bool enable)
switch_on(d->state, Throttled);
} else {
switch_off(d->state, Throttled);
- if (!(d->state.load() & Paused))
+ if (!(d->state.loadRelaxed() & Paused))
d->pausedWaitCondition.wakeAll();
}
}
@@ -201,13 +201,13 @@ void QFutureInterfaceBase::waitForResume()
{
// return early if possible to avoid taking the mutex lock.
{
- const int state = d->state.load();
+ const int state = d->state.loadRelaxed();
if (!(state & Paused) || (state & Canceled))
return;
}
QMutexLocker lock(&d->m_mutex);
- const int state = d->state.load();
+ const int state = d->state.loadRelaxed();
if (!(state & Paused) || (state & Canceled))
return;
@@ -256,7 +256,7 @@ bool QFutureInterfaceBase::isProgressUpdateNeeded() const
void QFutureInterfaceBase::reportStarted()
{
QMutexLocker locker(&d->m_mutex);
- if (d->state.load() & (Started|Canceled|Finished))
+ if (d->state.loadRelaxed() & (Started|Canceled|Finished))
return;
d->setState(State(Started | Running));
@@ -272,7 +272,7 @@ void QFutureInterfaceBase::reportCanceled()
void QFutureInterfaceBase::reportException(const QException &exception)
{
QMutexLocker locker(&d->m_mutex);
- if (d->state.load() & (Canceled|Finished))
+ if (d->state.loadRelaxed() & (Canceled|Finished))
return;
d->m_exceptionStore.setException(exception);
@@ -307,7 +307,7 @@ int QFutureInterfaceBase::expectedResultCount()
bool QFutureInterfaceBase::queryState(State state) const
{
- return d->state.load() & state;
+ return d->state.loadRelaxed() & state;
}
void QFutureInterfaceBase::waitForResult(int resultIndex)
@@ -352,7 +352,7 @@ void QFutureInterfaceBase::waitForFinished()
void QFutureInterfaceBase::reportResultsReady(int beginIndex, int endIndex)
{
- if (beginIndex == endIndex || (d->state.load() & (Canceled|Finished)))
+ if (beginIndex == endIndex || (d->state.loadRelaxed() & (Canceled|Finished)))
return;
d->waitCondition.wakeAll();
@@ -414,7 +414,7 @@ void QFutureInterfaceBase::setProgressValueAndText(int progressValue,
if (d->m_progressValue >= progressValue)
return;
- if (d->state.load() & (Canceled|Finished))
+ if (d->state.loadRelaxed() & (Canceled|Finished))
return;
if (d->internal_updateProgress(progressValue, progressText)) {
@@ -486,10 +486,10 @@ bool QFutureInterfaceBasePrivate::internal_waitForNextResult()
if (m_results.hasNextResult())
return true;
- while ((state.load() & QFutureInterfaceBase::Running) && m_results.hasNextResult() == false)
+ while ((state.loadRelaxed() & QFutureInterfaceBase::Running) && m_results.hasNextResult() == false)
waitCondition.wait(&m_mutex);
- return !(state.load() & QFutureInterfaceBase::Canceled) && m_results.hasNextResult();
+ return !(state.loadRelaxed() & QFutureInterfaceBase::Canceled) && m_results.hasNextResult();
}
bool QFutureInterfaceBasePrivate::internal_updateProgress(int progress,
@@ -512,8 +512,8 @@ bool QFutureInterfaceBasePrivate::internal_updateProgress(int progress,
void QFutureInterfaceBasePrivate::internal_setThrottled(bool enable)
{
// bail out if we are not changing the state
- if ((enable && (state.load() & QFutureInterfaceBase::Throttled))
- || (!enable && !(state.load() & QFutureInterfaceBase::Throttled)))
+ if ((enable && (state.loadRelaxed() & QFutureInterfaceBase::Throttled))
+ || (!enable && !(state.loadRelaxed() & QFutureInterfaceBase::Throttled)))
return;
// change the state
@@ -521,7 +521,7 @@ void QFutureInterfaceBasePrivate::internal_setThrottled(bool enable)
switch_on(state, QFutureInterfaceBase::Throttled);
} else {
switch_off(state, QFutureInterfaceBase::Throttled);
- if (!(state.load() & QFutureInterfaceBase::Paused))
+ if (!(state.loadRelaxed() & QFutureInterfaceBase::Paused))
pausedWaitCondition.wakeAll();
}
}
@@ -556,7 +556,7 @@ void QFutureInterfaceBasePrivate::connectOutputInterface(QFutureCallOutInterface
{
QMutexLocker locker(&m_mutex);
- if (state.load() & QFutureInterfaceBase::Started) {
+ if (state.loadRelaxed() & QFutureInterfaceBase::Started) {
interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Started));
interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ProgressRange,
m_progressMinimum,
@@ -576,13 +576,13 @@ void QFutureInterfaceBasePrivate::connectOutputInterface(QFutureCallOutInterface
it.batchedAdvance();
}
- if (state.load() & QFutureInterfaceBase::Paused)
+ if (state.loadRelaxed() & QFutureInterfaceBase::Paused)
interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Paused));
- if (state.load() & QFutureInterfaceBase::Canceled)
+ if (state.loadRelaxed() & QFutureInterfaceBase::Canceled)
interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Canceled));
- if (state.load() & QFutureInterfaceBase::Finished)
+ if (state.loadRelaxed() & QFutureInterfaceBase::Finished)
interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Finished));
outputConnections.append(interface);
@@ -601,7 +601,7 @@ void QFutureInterfaceBasePrivate::disconnectOutputInterface(QFutureCallOutInterf
void QFutureInterfaceBasePrivate::setState(QFutureInterfaceBase::State newState)
{
- state.store(newState);
+ state.storeRelaxed(newState);
}
QT_END_NAMESPACE
diff --git a/src/corelib/thread/qfutureinterface_p.h b/src/corelib/thread/qfutureinterface_p.h
index 63e534464f..b297dff633 100644
--- a/src/corelib/thread/qfutureinterface_p.h
+++ b/src/corelib/thread/qfutureinterface_p.h
@@ -144,11 +144,11 @@ public:
// Default ref counter for QFIBP
inline bool ref() { return m_refCount.ref(); }
inline bool deref() { return m_refCount.deref(); }
- inline int load() const { return m_refCount.load(); }
+ inline int load() const { return m_refCount.loadRelaxed(); }
// Ref counter for type T
inline bool refT() { return m_refCountT.ref(); }
inline bool derefT() { return m_refCountT.deref(); }
- inline int loadT() const { return m_refCountT.load(); }
+ inline int loadT() const { return m_refCountT.loadRelaxed(); }
private:
QAtomicInt m_refCount;
diff --git a/src/corelib/thread/qfuturewatcher.cpp b/src/corelib/thread/qfuturewatcher.cpp
index 4ee7693ace..06bc4740f9 100644
--- a/src/corelib/thread/qfuturewatcher.cpp
+++ b/src/corelib/thread/qfuturewatcher.cpp
@@ -84,8 +84,8 @@ QT_BEGIN_NAMESPACE
\snippet code/src_corelib_thread_qfuturewatcher.cpp 0
- Be aware that not all asynchronous computations can be canceled or paused.
- For example, the future returned by QtConcurrent::run() cannot be
+ Be aware that not all running asynchronous computations can be canceled or
+ paused. For example, the future returned by QtConcurrent::run() cannot be
canceled; but the future returned by QtConcurrent::mappedReduced() can.
QFutureWatcher<void> is specialized to not contain any of the result
@@ -124,9 +124,9 @@ QFutureWatcherBase::QFutureWatcherBase(QObject *parent)
progressRangeChanged(), progressTextChanged(), resultReadyAt(), and
resultsReadyAt() signals.
- Be aware that not all asynchronous computations can be canceled. For
- example, the QFuture returned by QtConcurrent::run() cannot be canceled;
- but the QFuture returned by QtConcurrent::mappedReduced() can.
+ Be aware that not all running asynchronous computations can be canceled.
+ For example, the QFuture returned by QtConcurrent::run() cannot be
+ canceled; but the QFuture returned by QtConcurrent::mappedReduced() can.
*/
void QFutureWatcherBase::cancel()
{
@@ -402,7 +402,7 @@ void QFutureWatcherBase::disconnectOutputInterface(bool pendingAssignment)
{
if (pendingAssignment) {
Q_D(QFutureWatcherBase);
- d->pendingResultsReady.store(0);
+ d->pendingResultsReady.storeRelaxed(0);
qDeleteAll(d->pendingCallOutEvents);
d->pendingCallOutEvents.clear();
d->finished = false; /* May soon be amended, during connectOutputInterface() */
@@ -441,7 +441,7 @@ void QFutureWatcherBasePrivate::sendCallOutEvent(QFutureCallOutEvent *event)
emit q->finished();
break;
case QFutureCallOutEvent::Canceled:
- pendingResultsReady.store(0);
+ pendingResultsReady.storeRelaxed(0);
emit q->canceled();
break;
case QFutureCallOutEvent::Paused:
@@ -466,7 +466,7 @@ void QFutureWatcherBasePrivate::sendCallOutEvent(QFutureCallOutEvent *event)
emit q->resultsReadyAt(beginIndex, endIndex);
- if (resultAtConnected.load() <= 0)
+ if (resultAtConnected.loadRelaxed() <= 0)
break;
for (int i = beginIndex; i < endIndex; ++i)
diff --git a/src/corelib/thread/qgenericatomic.h b/src/corelib/thread/qgenericatomic.h
index f8333e7de6..e9e5f3c74b 100644
--- a/src/corelib/thread/qgenericatomic.h
+++ b/src/corelib/thread/qgenericatomic.h
@@ -97,6 +97,18 @@ template <typename BaseClass> struct QGenericAtomicOps
}
template <typename T> static Q_ALWAYS_INLINE
+ T loadRelaxed(const T &_q_value) noexcept
+ {
+ return _q_value;
+ }
+
+ template <typename T, typename X> static Q_ALWAYS_INLINE
+ void storeRelaxed(T &_q_value, X newValue) noexcept
+ {
+ _q_value = newValue;
+ }
+
+ template <typename T> static Q_ALWAYS_INLINE
T loadAcquire(const T &_q_value) noexcept
{
T tmp = *static_cast<const volatile T *>(&_q_value);
@@ -190,7 +202,7 @@ template <typename BaseClass> struct QGenericAtomicOps
{
// implement fetchAndStore on top of testAndSet
Q_FOREVER {
- T tmp = load(_q_value);
+ T tmp = loadRelaxed(_q_value);
if (BaseClass::testAndSetRelaxed(_q_value, tmp, newValue))
return tmp;
}
@@ -225,7 +237,7 @@ template <typename BaseClass> struct QGenericAtomicOps
{
// implement fetchAndAdd on top of testAndSet
Q_FOREVER {
- T tmp = BaseClass::load(_q_value);
+ T tmp = BaseClass::loadRelaxed(_q_value);
if (BaseClass::testAndSetRelaxed(_q_value, tmp, T(tmp + valueToAdd)))
return tmp;
}
@@ -289,7 +301,7 @@ QT_WARNING_POP
T fetchAndAndRelaxed(T &_q_value, typename std::enable_if<QTypeInfo<T>::isIntegral, T>::type operand) noexcept
{
// implement fetchAndAnd on top of testAndSet
- T tmp = BaseClass::load(_q_value);
+ T tmp = BaseClass::loadRelaxed(_q_value);
Q_FOREVER {
if (BaseClass::testAndSetRelaxed(_q_value, tmp, T(tmp & operand), &tmp))
return tmp;
@@ -322,7 +334,7 @@ QT_WARNING_POP
T fetchAndOrRelaxed(T &_q_value, typename std::enable_if<QTypeInfo<T>::isIntegral, T>::type operand) noexcept
{
// implement fetchAndOr on top of testAndSet
- T tmp = BaseClass::load(_q_value);
+ T tmp = BaseClass::loadRelaxed(_q_value);
Q_FOREVER {
if (BaseClass::testAndSetRelaxed(_q_value, tmp, T(tmp | operand), &tmp))
return tmp;
@@ -355,7 +367,7 @@ QT_WARNING_POP
T fetchAndXorRelaxed(T &_q_value, typename std::enable_if<QTypeInfo<T>::isIntegral, T>::type operand) noexcept
{
// implement fetchAndXor on top of testAndSet
- T tmp = BaseClass::load(_q_value);
+ T tmp = BaseClass::loadRelaxed(_q_value);
Q_FOREVER {
if (BaseClass::testAndSetRelaxed(_q_value, tmp, T(tmp ^ operand), &tmp))
return tmp;
diff --git a/src/corelib/thread/qmutex.cpp b/src/corelib/thread/qmutex.cpp
index 4f55e50fe5..bd3a0fa7ba 100644
--- a/src/corelib/thread/qmutex.cpp
+++ b/src/corelib/thread/qmutex.cpp
@@ -179,7 +179,7 @@ public:
*/
QMutex::QMutex(RecursionMode mode)
{
- d_ptr.store(mode == Recursive ? new QRecursiveMutexPrivate : 0);
+ d_ptr.storeRelaxed(mode == Recursive ? new QRecursiveMutexPrivate : 0);
}
/*!
@@ -189,12 +189,12 @@ QMutex::QMutex(RecursionMode mode)
*/
QMutex::~QMutex()
{
- QMutexData *d = d_ptr.load();
+ QMutexData *d = d_ptr.loadRelaxed();
if (isRecursive()) {
delete static_cast<QRecursiveMutexPrivate *>(d);
} else if (d) {
#ifndef QT_LINUX_FUTEX
- if (d != dummyLocked() && static_cast<QMutexPrivate *>(d)->possiblyUnlocked.load()
+ if (d != dummyLocked() && static_cast<QMutexPrivate *>(d)->possiblyUnlocked.loadRelaxed()
&& tryLock()) {
unlock();
return;
@@ -517,7 +517,7 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT
}
QMutexPrivate *d = static_cast<QMutexPrivate *>(copy);
- if (timeout == 0 && !d->possiblyUnlocked.load())
+ if (timeout == 0 && !d->possiblyUnlocked.loadRelaxed())
return false;
// At this point we have a pointer to a QMutexPrivate. But the other thread
@@ -541,7 +541,7 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT
// is set to the BigNumber magic value set in unlockInternal()
int old_waiters;
do {
- old_waiters = d->waiters.load();
+ old_waiters = d->waiters.loadRelaxed();
if (old_waiters == -QMutexPrivate::BigNumber) {
// we are unlocking, and the thread that unlocks is about to change d to 0
// we try to acquire the mutex by changing to dummyLocked()
@@ -550,7 +550,7 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT
d->deref();
return true;
} else {
- Q_ASSERT(d != d_ptr.load()); //else testAndSetAcquire should have succeeded
+ Q_ASSERT(d != d_ptr.loadRelaxed()); //else testAndSetAcquire should have succeeded
// Mutex is likely to bo 0, we should continue the outer-loop,
// set old_waiters to the magic value of BigNumber
old_waiters = QMutexPrivate::BigNumber;
@@ -563,7 +563,7 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT
// The mutex was unlocked before we incremented waiters.
if (old_waiters != QMutexPrivate::BigNumber) {
//we did not break the previous loop
- Q_ASSERT(d->waiters.load() >= 1);
+ Q_ASSERT(d->waiters.loadRelaxed() >= 1);
d->waiters.deref();
}
d->deref();
@@ -572,11 +572,11 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT
if (d->wait(timeout)) {
// reset the possiblyUnlocked flag if needed (and deref its corresponding reference)
- if (d->possiblyUnlocked.load() && d->possiblyUnlocked.testAndSetRelaxed(true, false))
+ if (d->possiblyUnlocked.loadRelaxed() && d->possiblyUnlocked.testAndSetRelaxed(true, false))
d->deref();
d->derefWaiters(1);
//we got the lock. (do not deref)
- Q_ASSERT(d == d_ptr.load());
+ Q_ASSERT(d == d_ptr.loadRelaxed());
return true;
} else {
Q_ASSERT(timeout >= 0);
@@ -593,7 +593,7 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT
return false;
}
}
- Q_ASSERT(d_ptr.load() != 0);
+ Q_ASSERT(d_ptr.loadRelaxed() != 0);
return true;
}
@@ -618,7 +618,7 @@ void QBasicMutex::unlockInternal() noexcept
//there is no one waiting on this mutex anymore, set the mutex as unlocked (d = 0)
if (d_ptr.testAndSetRelease(d, 0)) {
// reset the possiblyUnlocked flag if needed (and deref its corresponding reference)
- if (d->possiblyUnlocked.load() && d->possiblyUnlocked.testAndSetRelaxed(true, false))
+ if (d->possiblyUnlocked.loadRelaxed() && d->possiblyUnlocked.testAndSetRelaxed(true, false))
d->deref();
}
d->derefWaiters(0);
@@ -657,20 +657,20 @@ QMutexPrivate *QMutexPrivate::allocate()
int i = freelist()->next();
QMutexPrivate *d = &(*freelist())[i];
d->id = i;
- Q_ASSERT(d->refCount.load() == 0);
+ Q_ASSERT(d->refCount.loadRelaxed() == 0);
Q_ASSERT(!d->recursive);
- Q_ASSERT(!d->possiblyUnlocked.load());
- Q_ASSERT(d->waiters.load() == 0);
- d->refCount.store(1);
+ Q_ASSERT(!d->possiblyUnlocked.loadRelaxed());
+ Q_ASSERT(d->waiters.loadRelaxed() == 0);
+ d->refCount.storeRelaxed(1);
return d;
}
void QMutexPrivate::release()
{
Q_ASSERT(!recursive);
- Q_ASSERT(refCount.load() == 0);
- Q_ASSERT(!possiblyUnlocked.load());
- Q_ASSERT(waiters.load() == 0);
+ Q_ASSERT(refCount.loadRelaxed() == 0);
+ Q_ASSERT(!possiblyUnlocked.loadRelaxed());
+ Q_ASSERT(waiters.loadRelaxed() == 0);
freelist()->release(id);
}
@@ -680,7 +680,7 @@ void QMutexPrivate::derefWaiters(int value) noexcept
int old_waiters;
int new_waiters;
do {
- old_waiters = waiters.load();
+ old_waiters = waiters.loadRelaxed();
new_waiters = old_waiters;
if (new_waiters < 0) {
new_waiters += QMutexPrivate::BigNumber;
@@ -696,7 +696,7 @@ void QMutexPrivate::derefWaiters(int value) noexcept
inline bool QRecursiveMutexPrivate::lock(int timeout) QT_MUTEX_LOCK_NOEXCEPT
{
Qt::HANDLE self = QThread::currentThreadId();
- if (owner.load() == self) {
+ if (owner.loadRelaxed() == self) {
++count;
Q_ASSERT_X(count != 0, "QMutex::lock", "Overflow in recursion counter");
return true;
@@ -709,7 +709,7 @@ inline bool QRecursiveMutexPrivate::lock(int timeout) QT_MUTEX_LOCK_NOEXCEPT
}
if (success)
- owner.store(self);
+ owner.storeRelaxed(self);
return success;
}
@@ -721,7 +721,7 @@ inline void QRecursiveMutexPrivate::unlock() noexcept
if (count > 0) {
count--;
} else {
- owner.store(0);
+ owner.storeRelaxed(0);
mutex.QBasicMutex::unlock();
}
}
diff --git a/src/corelib/thread/qmutex.h b/src/corelib/thread/qmutex.h
index d7796092d1..0de0869cb2 100644
--- a/src/corelib/thread/qmutex.h
+++ b/src/corelib/thread/qmutex.h
@@ -81,7 +81,7 @@ public:
// BasicLockable concept
inline void unlock() noexcept {
- Q_ASSERT(d_ptr.load()); //mutex must be locked
+ Q_ASSERT(d_ptr.loadRelaxed()); //mutex must be locked
if (!fastTryUnlock())
unlockInternal();
}
diff --git a/src/corelib/thread/qmutex_linux.cpp b/src/corelib/thread/qmutex_linux.cpp
index b006ff1033..3270875471 100644
--- a/src/corelib/thread/qmutex_linux.cpp
+++ b/src/corelib/thread/qmutex_linux.cpp
@@ -149,7 +149,7 @@ bool lockInternal_helper(QBasicAtomicPointer<QMutexData> &d_ptr, int timeout = -
}
}
- Q_ASSERT(d_ptr.load());
+ Q_ASSERT(d_ptr.loadRelaxed());
return true;
}
@@ -169,7 +169,7 @@ bool QBasicMutex::lockInternal(int timeout) noexcept
void QBasicMutex::unlockInternal() noexcept
{
- QMutexData *d = d_ptr.load();
+ QMutexData *d = d_ptr.loadRelaxed();
Q_ASSERT(d); //we must be locked
Q_ASSERT(d != dummyLocked()); // testAndSetRelease(dummyLocked(), 0) failed
Q_UNUSED(d);
diff --git a/src/corelib/thread/qmutex_p.h b/src/corelib/thread/qmutex_p.h
index 5025f836b9..048d8707c4 100644
--- a/src/corelib/thread/qmutex_p.h
+++ b/src/corelib/thread/qmutex_p.h
@@ -99,21 +99,21 @@ public:
int id;
bool ref() {
- Q_ASSERT(refCount.load() >= 0);
+ Q_ASSERT(refCount.loadRelaxed() >= 0);
int c;
do {
- c = refCount.load();
+ c = refCount.loadRelaxed();
if (c == 0)
return false;
} while (!refCount.testAndSetRelaxed(c, c + 1));
- Q_ASSERT(refCount.load() >= 0);
+ Q_ASSERT(refCount.loadRelaxed() >= 0);
return true;
}
void deref() {
- Q_ASSERT(refCount.load() >= 0);
+ Q_ASSERT(refCount.loadRelaxed() >= 0);
if (!refCount.deref())
release();
- Q_ASSERT(refCount.load() >= 0);
+ Q_ASSERT(refCount.loadRelaxed() >= 0);
}
void release();
static QMutexPrivate *allocate();
diff --git a/src/corelib/thread/qmutexpool.cpp b/src/corelib/thread/qmutexpool.cpp
index 3f9e8da942..2a02197859 100644
--- a/src/corelib/thread/qmutexpool.cpp
+++ b/src/corelib/thread/qmutexpool.cpp
@@ -93,7 +93,7 @@ QMutexPool::QMutexPool(QMutex::RecursionMode recursionMode, int size)
: mutexes(size), recursionMode(recursionMode)
{
for (int index = 0; index < mutexes.count(); ++index) {
- mutexes[index].store(0);
+ mutexes[index].storeRelaxed(0);
}
}
@@ -104,7 +104,7 @@ QMutexPool::QMutexPool(QMutex::RecursionMode recursionMode, int size)
QMutexPool::~QMutexPool()
{
for (int index = 0; index < mutexes.count(); ++index)
- delete mutexes[index].load();
+ delete mutexes[index].loadRelaxed();
}
/*!
@@ -131,7 +131,7 @@ QMutex *QMutexPool::createMutex(int index)
QMutex *newMutex = new QMutex(recursionMode);
if (!mutexes[index].testAndSetRelease(0, newMutex))
delete newMutex;
- return mutexes[index].load();
+ return mutexes[index].loadRelaxed();
}
/*!
diff --git a/src/corelib/thread/qmutexpool_p.h b/src/corelib/thread/qmutexpool_p.h
index 89d006ac29..1a47231abc 100644
--- a/src/corelib/thread/qmutexpool_p.h
+++ b/src/corelib/thread/qmutexpool_p.h
@@ -68,7 +68,7 @@ public:
inline QMutex *get(const void *address) {
int index = uint(quintptr(address)) % mutexes.count();
- QMutex *m = mutexes[index].load();
+ QMutex *m = mutexes[index].loadRelaxed();
if (m)
return m;
else
diff --git a/src/corelib/thread/qreadwritelock.cpp b/src/corelib/thread/qreadwritelock.cpp
index d7cf7a7284..30e9b95a52 100644
--- a/src/corelib/thread/qreadwritelock.cpp
+++ b/src/corelib/thread/qreadwritelock.cpp
@@ -143,7 +143,7 @@ inline bool isUncontendedLocked(const QReadWriteLockPrivate *d)
QReadWriteLock::QReadWriteLock(RecursionMode recursionMode)
: d_ptr(recursionMode == Recursive ? new QReadWriteLockPrivate(true) : nullptr)
{
- Q_ASSERT_X(!(quintptr(d_ptr.load()) & StateMask), "QReadWriteLock::QReadWriteLock", "bad d_ptr alignment");
+ Q_ASSERT_X(!(quintptr(d_ptr.loadRelaxed()) & StateMask), "QReadWriteLock::QReadWriteLock", "bad d_ptr alignment");
}
/*!
@@ -154,7 +154,7 @@ QReadWriteLock::QReadWriteLock(RecursionMode recursionMode)
*/
QReadWriteLock::~QReadWriteLock()
{
- auto d = d_ptr.load();
+ auto d = d_ptr.loadRelaxed();
if (isUncontendedLocked(d)) {
qWarning("QReadWriteLock: destroying locked QReadWriteLock");
return;
@@ -263,7 +263,7 @@ bool QReadWriteLock::tryLockForRead(int timeout)
return d->recursiveLockForRead(timeout);
QMutexLocker lock(&d->mutex);
- if (d != d_ptr.load()) {
+ if (d != d_ptr.loadRelaxed()) {
// d_ptr has changed: this QReadWriteLock was unlocked before we had
// time to lock d->mutex.
// We are holding a lock to a mutex within a QReadWriteLockPrivate
@@ -370,7 +370,7 @@ bool QReadWriteLock::tryLockForWrite(int timeout)
return d->recursiveLockForWrite(timeout);
QMutexLocker lock(&d->mutex);
- if (d != d_ptr.load()) {
+ if (d != d_ptr.loadRelaxed()) {
// The mutex was unlocked before we had time to lock the mutex.
// We are holding to a mutex within a QReadWriteLockPrivate that is already released
// (or even is already re-used) but that's ok because the QFreeList never frees them.
@@ -433,7 +433,7 @@ void QReadWriteLock::unlock()
if (d->waitingReaders || d->waitingWriters) {
d->unlock();
} else {
- Q_ASSERT(d_ptr.load() == d); // should not change when we still hold the mutex
+ Q_ASSERT(d_ptr.loadRelaxed() == d); // should not change when we still hold the mutex
d_ptr.storeRelease(nullptr);
d->release();
}
@@ -444,7 +444,7 @@ void QReadWriteLock::unlock()
/*! \internal Helper for QWaitCondition::wait */
QReadWriteLock::StateForWaitCondition QReadWriteLock::stateForWaitCondition() const
{
- QReadWriteLockPrivate *d = d_ptr.load();
+ QReadWriteLockPrivate *d = d_ptr.loadRelaxed();
switch (quintptr(d) & StateMask) {
case StateLockedForRead: return LockedForRead;
case StateLockedForWrite: return LockedForWrite;
diff --git a/src/corelib/thread/qsemaphore.cpp b/src/corelib/thread/qsemaphore.cpp
index 2e0b6f2bc0..d4fb756b94 100644
--- a/src/corelib/thread/qsemaphore.cpp
+++ b/src/corelib/thread/qsemaphore.cpp
@@ -264,7 +264,7 @@ template <bool IsTimed> bool futexSemaphoreTryAcquire(QBasicAtomicInteger<quintp
if (futexHasWaiterCount) {
// decrement the number of threads waiting
- Q_ASSERT(futexHigh32(&u)->load() & 0x7fffffffU);
+ Q_ASSERT(futexHigh32(&u)->loadRelaxed() & 0x7fffffffU);
u.fetchAndSubRelaxed(oneWaiter);
}
return false;
@@ -293,7 +293,7 @@ QSemaphore::QSemaphore(int n)
quintptr nn = unsigned(n);
if (futexHasWaiterCount)
nn |= quint64(nn) << 32; // token count replicated in high word
- u.store(nn);
+ u.storeRelaxed(nn);
} else {
d = new QSemaphorePrivate(n);
}
@@ -425,7 +425,7 @@ void QSemaphore::release(int n)
int QSemaphore::available() const
{
if (futexAvailable())
- return futexAvailCounter(u.load());
+ return futexAvailCounter(u.loadRelaxed());
QMutexLocker locker(&d->mutex);
return d->avail;
diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp
index b9b9f3e354..1eac7db11d 100644
--- a/src/corelib/thread/qthread.cpp
+++ b/src/corelib/thread/qthread.cpp
@@ -65,7 +65,7 @@ QThreadData::QThreadData(int initialRefCount)
QThreadData::~QThreadData()
{
- Q_ASSERT(_ref.load() == 0);
+ Q_ASSERT(_ref.loadRelaxed() == 0);
// In the odd case that Qt is running on a secondary thread, the main
// thread instance will have been dereffed asunder because of the deref in
@@ -105,7 +105,7 @@ void QThreadData::ref()
{
#if QT_CONFIG(thread)
(void) _ref.ref();
- Q_ASSERT(_ref.load() != 0);
+ Q_ASSERT(_ref.loadRelaxed() != 0);
#endif
}
@@ -878,11 +878,11 @@ QThreadData *QThreadData::current(bool createIfNecessary)
if (!data && createIfNecessary) {
data = new QThreadData;
data->thread = new QAdoptedThread(data);
- data->threadId.store(Qt::HANDLE(data->thread));
+ data->threadId.storeRelaxed(Qt::HANDLE(data->thread));
data->deref();
data->isAdopted = true;
if (!QCoreApplicationPrivate::theMainThread)
- QCoreApplicationPrivate::theMainThread = data->thread.load();
+ QCoreApplicationPrivate::theMainThread = data->thread.loadRelaxed();
}
return data;
}
@@ -925,7 +925,7 @@ QThreadPrivate::~QThreadPrivate()
QAbstractEventDispatcher *QThread::eventDispatcher() const
{
Q_D(const QThread);
- return d->data->eventDispatcher.load();
+ return d->data->eventDispatcher.loadRelaxed();
}
/*!
diff --git a/src/corelib/thread/qthread_p.h b/src/corelib/thread/qthread_p.h
index 209225de98..b2d1628e6e 100644
--- a/src/corelib/thread/qthread_p.h
+++ b/src/corelib/thread/qthread_p.h
@@ -195,6 +195,9 @@ public:
int waiters;
bool terminationEnabled, terminatePending;
#endif // Q_OS_WIN
+#ifdef Q_OS_WASM
+ static int idealThreadCount;
+#endif
QThreadData *data;
static QAbstractEventDispatcher *createEventDispatcher(QThreadData *data);
@@ -254,11 +257,11 @@ public:
void ref();
void deref();
inline bool hasEventDispatcher() const
- { return eventDispatcher.load() != nullptr; }
+ { return eventDispatcher.loadRelaxed() != nullptr; }
QAbstractEventDispatcher *createEventDispatcher();
QAbstractEventDispatcher *ensureEventDispatcher()
{
- QAbstractEventDispatcher *ed = eventDispatcher.load();
+ QAbstractEventDispatcher *ed = eventDispatcher.loadRelaxed();
if (Q_LIKELY(ed))
return ed;
return createEventDispatcher();
diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp
index 3d4c906dc2..89efbff6aa 100644
--- a/src/corelib/thread/qthread_unix.cpp
+++ b/src/corelib/thread/qthread_unix.cpp
@@ -100,10 +100,6 @@
#include <sys/neutrino.h>
#endif
-#if defined(Q_OS_WASM)
-#include <emscripten/val.h>
-#endif
-
QT_BEGIN_NAMESPACE
#if QT_CONFIG(thread)
@@ -119,6 +115,9 @@ enum { ThreadPriorityResetFlag = 0x80000000 };
#if defined(Q_CC_XLC) || defined (Q_CC_SUN)
#define HAVE_TLS
#endif
+#if defined(Q_OS_RTEMS)
+#define HAVE_TLS
+#endif
#ifdef HAVE_TLS
static __thread QThreadData *currentThreadData = 0;
@@ -253,9 +252,9 @@ QThreadData *QThreadData::current(bool createIfNecessary)
}
data->deref();
data->isAdopted = true;
- data->threadId.store(to_HANDLE(pthread_self()));
+ data->threadId.storeRelaxed(to_HANDLE(pthread_self()));
if (!QCoreApplicationPrivate::theMainThread)
- QCoreApplicationPrivate::theMainThread = data->thread.load();
+ QCoreApplicationPrivate::theMainThread = data->thread.loadRelaxed();
}
return data;
}
@@ -335,7 +334,7 @@ void *QThreadPrivate::start(void *arg)
thr->d_func()->setPriority(QThread::Priority(thr->d_func()->priority & ~ThreadPriorityResetFlag));
}
- data->threadId.store(to_HANDLE(pthread_self()));
+ data->threadId.storeRelaxed(to_HANDLE(pthread_self()));
set_thread_data(data);
data->ref();
@@ -405,7 +404,7 @@ void QThreadPrivate::finish(void *arg)
QThreadStorageData::finish((void **)data);
locker.relock();
- QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher.load();
+ QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher.loadRelaxed();
if (eventDispatcher) {
d->data->eventDispatcher = 0;
locker.unlock();
@@ -454,6 +453,10 @@ Qt::HANDLE QThread::currentThreadId() noexcept
# define _SC_NPROCESSORS_ONLN 84
#endif
+#ifdef Q_OS_WASM
+int QThreadPrivate::idealThreadCount = 1;
+#endif
+
int QThread::idealThreadCount() noexcept
{
int cores = 1;
@@ -503,7 +506,7 @@ int QThread::idealThreadCount() noexcept
cores = 1;
# endif
#elif defined(Q_OS_WASM)
- cores = emscripten::val::global("navigator")["hardwareConcurrency"].as<int>();
+ cores = QThreadPrivate::idealThreadCount;
#else
// the rest: Linux, Solaris, AIX, Tru64
cores = (int)sysconf(_SC_NPROCESSORS_ONLN);
@@ -724,6 +727,12 @@ void QThread::start(Priority priority)
}
}
+#ifdef Q_OS_INTEGRITY
+ if (Q_LIKELY(objectName().isEmpty()))
+ pthread_attr_setthreadname(&attr, metaObject()->className());
+ else
+ pthread_attr_setthreadname(&attr, objectName().toLocal8Bit());
+#endif
pthread_t threadId;
int code = pthread_create(&threadId, &attr, QThreadPrivate::start, this);
if (code == EPERM) {
@@ -734,7 +743,7 @@ void QThread::start(Priority priority)
#endif
code = pthread_create(&threadId, &attr, QThreadPrivate::start, this);
}
- d->data->threadId.store(to_HANDLE(threadId));
+ d->data->threadId.storeRelaxed(to_HANDLE(threadId));
pthread_attr_destroy(&attr);
@@ -743,7 +752,7 @@ void QThread::start(Priority priority)
d->running = false;
d->finished = false;
- d->data->threadId.store(nullptr);
+ d->data->threadId.storeRelaxed(nullptr);
}
}
@@ -753,10 +762,10 @@ void QThread::terminate()
Q_D(QThread);
QMutexLocker locker(&d->mutex);
- if (!d->data->threadId.load())
+ if (!d->data->threadId.loadRelaxed())
return;
- int code = pthread_cancel(from_HANDLE<pthread_t>(d->data->threadId.load()));
+ int code = pthread_cancel(from_HANDLE<pthread_t>(d->data->threadId.loadRelaxed()));
if (code) {
qErrnoWarning(code, "QThread::start: Thread termination error");
}
@@ -768,7 +777,7 @@ bool QThread::wait(unsigned long time)
Q_D(QThread);
QMutexLocker locker(&d->mutex);
- if (from_HANDLE<pthread_t>(d->data->threadId.load()) == pthread_self()) {
+ if (from_HANDLE<pthread_t>(d->data->threadId.loadRelaxed()) == pthread_self()) {
qWarning("QThread::wait: Thread tried to wait on itself");
return false;
}
@@ -810,7 +819,7 @@ void QThreadPrivate::setPriority(QThread::Priority threadPriority)
int sched_policy;
sched_param param;
- if (pthread_getschedparam(from_HANDLE<pthread_t>(data->threadId.load()), &sched_policy, &param) != 0) {
+ if (pthread_getschedparam(from_HANDLE<pthread_t>(data->threadId.loadRelaxed()), &sched_policy, &param) != 0) {
// failed to get the scheduling policy, don't bother setting
// the priority
qWarning("QThread::setPriority: Cannot get scheduler parameters");
@@ -826,15 +835,15 @@ void QThreadPrivate::setPriority(QThread::Priority threadPriority)
}
param.sched_priority = prio;
- int status = pthread_setschedparam(from_HANDLE<pthread_t>(data->threadId.load()), sched_policy, &param);
+ int status = pthread_setschedparam(from_HANDLE<pthread_t>(data->threadId.loadRelaxed()), sched_policy, &param);
# ifdef SCHED_IDLE
// were we trying to set to idle priority and failed?
if (status == -1 && sched_policy == SCHED_IDLE && errno == EINVAL) {
// reset to lowest priority possible
- pthread_getschedparam(from_HANDLE<pthread_t>(data->threadId.load()), &sched_policy, &param);
+ pthread_getschedparam(from_HANDLE<pthread_t>(data->threadId.loadRelaxed()), &sched_policy, &param);
param.sched_priority = sched_get_priority_min(sched_policy);
- pthread_setschedparam(from_HANDLE<pthread_t>(data->threadId.load()), sched_policy, &param);
+ pthread_setschedparam(from_HANDLE<pthread_t>(data->threadId.loadRelaxed()), sched_policy, &param);
}
# else
Q_UNUSED(status);
diff --git a/src/corelib/thread/qthread_win.cpp b/src/corelib/thread/qthread_win.cpp
index ee73280707..f8a0b0abaa 100644
--- a/src/corelib/thread/qthread_win.cpp
+++ b/src/corelib/thread/qthread_win.cpp
@@ -138,11 +138,11 @@ QThreadData *QThreadData::current(bool createIfNecessary)
}
threadData->deref();
threadData->isAdopted = true;
- threadData->threadId.store(reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId())));
+ threadData->threadId.storeRelaxed(reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId())));
#ifndef Q_OS_WINRT
if (!QCoreApplicationPrivate::theMainThread) {
- QCoreApplicationPrivate::theMainThread = threadData->thread.load();
+ QCoreApplicationPrivate::theMainThread = threadData->thread.loadRelaxed();
} else {
#else
// for winrt the main thread is set explicitly in QCoreApplication's constructor as the
@@ -184,9 +184,9 @@ void QThreadData::setMainThread()
}
threadData->deref();
threadData->isAdopted = true;
- threadData->threadId.store(reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId())));
+ threadData->threadId.storeRelaxed(reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId())));
}
- QCoreApplicationPrivate::theMainThread = threadData->thread.load();
+ QCoreApplicationPrivate::theMainThread = threadData->thread.loadRelaxed();
}
#endif
@@ -379,7 +379,7 @@ unsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSE QThreadPrivate::start(voi
qt_create_tls();
TlsSetValue(qt_current_thread_data_tls_index, data);
- data->threadId.store(reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId())));
+ data->threadId.storeRelaxed(reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId())));
QThread::setTerminationEnabled(false);
@@ -421,7 +421,7 @@ void QThreadPrivate::finish(void *arg, bool lockAnyway) noexcept
QThreadStorageData::finish(tls_data);
locker.relock();
- QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher.load();
+ QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher.loadRelaxed();
if (eventDispatcher) {
d->data->eventDispatcher = 0;
locker.unlock();
diff --git a/src/corelib/thread/qthreadstorage.cpp b/src/corelib/thread/qthreadstorage.cpp
index 8b82118a5c..fdc484d2d2 100644
--- a/src/corelib/thread/qthreadstorage.cpp
+++ b/src/corelib/thread/qthreadstorage.cpp
@@ -126,7 +126,7 @@ void **QThreadStorageData::get() const
DEBUG_MSG("QThreadStorageData: Returning storage %d, data %p, for thread %p",
id,
*v,
- data->thread.load());
+ data->thread.loadRelaxed());
return *v ? v : 0;
}
@@ -148,7 +148,7 @@ void **QThreadStorageData::set(void *p)
DEBUG_MSG("QThreadStorageData: Deleting previous storage %d, data %p, for thread %p",
id,
value,
- data->thread.load());
+ data->thread.loadRelaxed());
QMutexLocker locker(&destructorsMutex);
DestructorMap *destr = destructors();
@@ -164,7 +164,7 @@ void **QThreadStorageData::set(void *p)
// store new data
value = p;
- DEBUG_MSG("QThreadStorageData: Set storage %d for thread %p to %p", id, data->thread.load(), p);
+ DEBUG_MSG("QThreadStorageData: Set storage %d for thread %p to %p", id, data->thread.loadRelaxed(), p);
return &value;
}
diff --git a/src/corelib/time/qdatetime.cpp b/src/corelib/time/qdatetime.cpp
index 9220d210f1..e34ce71212 100644
--- a/src/corelib/time/qdatetime.cpp
+++ b/src/corelib/time/qdatetime.cpp
@@ -334,19 +334,17 @@ static constexpr int daysInUsualMonth(int month) // (February isn't usual.)
\brief The QDate class provides date functions.
- A QDate object encodes a calendar date, i.e. year, month, and day numbers,
- in the proleptic Gregorian calendar by default. It can read the current date
- from the system clock. It provides functions for comparing dates, and for
- manipulating dates. For example, it is possible to add and subtract days,
- months, and years to dates.
+ A QDate object represents a particular date. This can be expressed as a
+ calendar date, i.e. year, month, and day numbers, in the proleptic Gregorian
+ calendar.
A QDate object is typically created by giving the year, month, and day
- numbers explicitly. Note that QDate interprets two digit years as presented,
- i.e., as years 0 through 99, without adding any offset. A QDate can also be
- constructed with the static function currentDate(), which creates a QDate
- object containing the system clock's date. An explicit date can also be set
- using setDate(). The fromString() function returns a QDate given a string
- and a date format which is used to interpret the date within the string.
+ numbers explicitly. Note that QDate interprets year numbers less than 100 as
+ presented, i.e., as years 1 through 99, without adding any offset. The
+ static function currentDate() creates a QDate object containing the date
+ read from the system clock. An explicit date can also be set using
+ setDate(). The fromString() function returns a QDate given a string and a
+ date format which is used to interpret the date within the string.
The year(), month(), and day() functions provide access to the
year, month, and day numbers. Also, dayOfWeek() and dayOfYear()
@@ -380,7 +378,7 @@ static constexpr int daysInUsualMonth(int month) // (February isn't usual.)
every day in a contiguous range, with 24 November 4714 BCE in the Gregorian
calendar being Julian Day 0 (1 January 4713 BCE in the Julian calendar).
As well as being an efficient and accurate way of storing an absolute date,
- it is suitable for converting a Date into other calendar systems such as
+ it is suitable for converting a date into other calendar systems such as
Hebrew, Islamic or Chinese. The Julian Day number can be obtained using
QDate::toJulianDay() and can be set using QDate::fromJulianDay().
@@ -615,7 +613,7 @@ int QDate::weekNumber(int *yearNumber) const
Q_ASSERT(week == 53 || week == 1);
}
- if (yearNumber != 0)
+ if (yearNumber)
*yearNumber = year;
return week;
}
@@ -1516,19 +1514,17 @@ QDate QDate::fromString(const QString &string, Qt::DateFormat format)
if (parts.count() != 4)
return QDate();
- QStringRef monthName = parts.at(1);
- const int month = fromShortMonthName(monthName);
- if (month == -1) {
- // Month name matches neither English nor other localised name.
- return QDate();
- }
-
bool ok = false;
int year = parts.at(3).toInt(&ok);
- if (!ok)
+ int day = ok ? parts.at(2).toInt(&ok) : 0;
+ if (!ok || !day)
return QDate();
- return QDate(year, month, parts.at(2).toInt());
+ const int month = fromShortMonthName(parts.at(1));
+ if (month == -1) // Month name matches no English or localised name.
+ return QDate();
+
+ return QDate(year, month, day);
}
#endif // textdate
case Qt::ISODate: {
@@ -1695,12 +1691,10 @@ bool QDate::isLeapYear(int y)
Unlike QDateTime, QTime knows nothing about time zones or
daylight-saving time (DST).
- A QTime object is typically created either by giving the number
- of hours, minutes, seconds, and milliseconds explicitly, or by
- using the static function currentTime(), which creates a QTime
- object that contains the system's local time. Note that the
- accuracy depends on the accuracy of the underlying operating
- system; not all systems provide 1-millisecond accuracy.
+ A QTime object is typically created either by giving the number of hours,
+ minutes, seconds, and milliseconds explicitly, or by using the static
+ function currentTime(), which creates a QTime object that represents the
+ system's local time.
The hour(), minute(), second(), and msec() functions provide
access to the number of hours, minutes, seconds, and milliseconds
@@ -2157,6 +2151,12 @@ int QTime::msecsTo(const QTime &t) const
Note that the accuracy depends on the accuracy of the underlying
operating system; not all systems provide 1-millisecond accuracy.
+
+ Furthermore, currentTime() only increases within each day; it shall drop by
+ 24 hours each time midnight passes; and, beside this, changes in it may not
+ correspond to elapsed time, if a daylight-saving transition intervenes.
+
+ \sa QDateTime::currentDateTime(), QDateTime::currentDateTimeUtc()
*/
#if QT_CONFIG(datestring)
@@ -2265,7 +2265,7 @@ QTime QTime::fromString(const QString &string, Qt::DateFormat format)
case Qt::ISODateWithMs:
case Qt::TextDate:
default:
- return fromIsoTimeString(QStringRef(&string), format, 0);
+ return fromIsoTimeString(QStringRef(&string), format, nullptr);
}
}
@@ -2512,7 +2512,7 @@ int QDateTimeParser::startsWithLocalTimeZone(const QStringRef name)
// then null date/time will be returned, you should adjust the date first if
// you need a guaranteed result.
static qint64 qt_mktime(QDate *date, QTime *time, QDateTimePrivate::DaylightStatus *daylightStatus,
- QString *abbreviation, bool *ok = 0)
+ QString *abbreviation, bool *ok = nullptr)
{
const qint64 msec = time->msec();
int yy, mm, dd;
@@ -2601,7 +2601,7 @@ static bool qt_localtime(qint64 msecsSinceEpoch, QDate *localDate, QTime *localT
#if QT_CONFIG(thread) && defined(_POSIX_THREAD_SAFE_FUNCTIONS)
// Use the reentrant version of localtime() where available
// as is thread-safe and doesn't use a shared static data area
- tm *res = 0;
+ tm *res = nullptr;
res = localtime_r(&secsSinceEpoch, &local);
if (res)
valid = true;
@@ -2611,7 +2611,7 @@ static bool qt_localtime(qint64 msecsSinceEpoch, QDate *localDate, QTime *localT
#else
// Returns shared static data which may be overwritten at any time
// So copy the result asap
- tm *res = 0;
+ tm *res = nullptr;
res = localtime(&secsSinceEpoch);
if (res) {
local = *res;
@@ -2674,7 +2674,7 @@ static qint64 timeToMSecs(const QDate &date, const QTime &time)
// Convert an MSecs Since Epoch into Local Time
static bool epochMSecsToLocalTime(qint64 msecs, QDate *localDate, QTime *localTime,
- QDateTimePrivate::DaylightStatus *daylightStatus = 0)
+ QDateTimePrivate::DaylightStatus *daylightStatus = nullptr)
{
if (msecs < 0) {
// Docs state any LocalTime before 1970-01-01 will *not* have any Daylight Time applied
@@ -2714,8 +2714,8 @@ static bool epochMSecsToLocalTime(qint64 msecs, QDate *localDate, QTime *localTi
// values from mktime for the adjusted local date and time.
static qint64 localMSecsToEpochMSecs(qint64 localMsecs,
QDateTimePrivate::DaylightStatus *daylightStatus,
- QDate *localDate = 0, QTime *localTime = 0,
- QString *abbreviation = 0)
+ QDate *localDate = nullptr, QTime *localTime = nullptr,
+ QString *abbreviation = nullptr)
{
QDate dt;
QTime tm;
@@ -3018,7 +3018,7 @@ static void setDateTime(QDateTimeData &d, const QDate &date, const QTime &time)
if (!useTime.isValid() && date.isValid())
useTime = QTime::fromMSecsSinceStartOfDay(0);
- QDateTimePrivate::StatusFlags newStatus = 0;
+ QDateTimePrivate::StatusFlags newStatus = { };
// Set date value and status
qint64 days = 0;
@@ -3096,7 +3096,7 @@ inline QDateTime::Data::Data(Qt::TimeSpec spec)
// the structure is too small, we need to detach
d = new QDateTimePrivate;
d->ref.ref();
- d->m_status = mergeSpec(0, spec);
+ d->m_status = mergeSpec(nullptr, spec);
}
}
@@ -3181,13 +3181,13 @@ inline void QDateTime::Data::detach()
x->m_status = QDateTimePrivate::StatusFlag(data.status & ~QDateTimePrivate::ShortData);
x->m_msecs = data.msecs;
} else {
- if (d->ref.load() == 1)
+ if (d->ref.loadRelaxed() == 1)
return;
x = new QDateTimePrivate(*d);
}
- x->ref.store(1);
+ x->ref.storeRelaxed(1);
if (!wasShort && !d->ref.deref())
delete d;
d = x;
@@ -3203,7 +3203,7 @@ inline QDateTimePrivate *QDateTime::Data::operator->()
{
// should we attempt to detach here?
Q_ASSERT(!isShort());
- Q_ASSERT(d->ref.load() == 1);
+ Q_ASSERT(d->ref.loadRelaxed() == 1);
return d;
}
@@ -3272,15 +3272,30 @@ inline qint64 QDateTimePrivate::zoneMSecsToEpochMSecs(qint64 zoneMSecs, const QT
provides functions for comparing datetimes and for manipulating a
datetime by adding a number of seconds, days, months, or years.
- A QDateTime object is typically created either by giving a date
- and time explicitly in the constructor, or by using the static
- function currentDateTime() that returns a QDateTime object set
- to the system clock's time. The date and time can be changed with
- setDate() and setTime(). A datetime can also be set using the
- setTime_t() function that takes a POSIX-standard "number of
- seconds since 00:00:00 on January 1, 1970" value. The fromString()
- function returns a QDateTime, given a string and a date format
- used to interpret the date within the string.
+ QDateTime can describe datetimes with respect to \l{Qt::LocalTime}{local
+ time}, to \l{Qt::UTC}{UTC}, to a specified \l{Qt::OffsetFromUTC}{offset
+ from UTC} or to a specified \l{{Qt::TimeZone}{time zone}, in conjunction
+ with the QTimeZone class. For example, a time zone of "Europe/Berlin" will
+ apply the daylight-saving rules as used in Germany since 1970. In contrast,
+ an offset from UTC of +3600 seconds is one hour ahead of UTC (usually
+ written in ISO standard notation as "UTC+01:00"), with no daylight-saving
+ offset or changes. When using either local time or a specified time zone,
+ time-zone transitions such as the starts and ends of daylight-saving time
+ (DST) are taken into account. The choice of system used to represent a
+ datetime is described as its "timespec".
+
+ A QDateTime object is typically created either by giving a date and time
+ explicitly in the constructor, or by using a static function such as
+ currentDateTime() or fromMSecsSinceEpoch(). The date and time can be changed
+ with setDate() and setTime(). A datetime can also be set using the
+ setMSecsSinceEpoch() function that takes the time, in milliseconds, since
+ 00:00:00 on January 1, 1970. The fromString() function returns a QDateTime,
+ given a string and a date format used to interpret the date within the
+ string.
+
+ QDateTime::currentDateTime() returns a QDateTime that expresses the current
+ time with respect to local time. QDateTime::currentDateTimeUtc() returns a
+ QDateTime that expresses the current time with respect to UTC.
The date() and time() functions provide access to the date and
time parts of the datetime. The same information is provided in
@@ -3291,18 +3306,20 @@ inline qint64 QDateTimePrivate::zoneMSecsToEpochMSecs(qint64 zoneMSecs, const QT
later.
You can increment (or decrement) a datetime by a given number of
- milliseconds using addMSecs(), seconds using addSecs(), or days
- using addDays(). Similarly, you can use addMonths() and addYears().
- The daysTo() function returns the number of days between two datetimes,
- secsTo() returns the number of seconds between two datetimes, and
- msecsTo() returns the number of milliseconds between two datetimes.
-
- QDateTime can store datetimes as \l{Qt::LocalTime}{local time} or
- as \l{Qt::UTC}{UTC}. QDateTime::currentDateTime() returns a
- QDateTime expressed as local time; use toUTC() to convert it to
- UTC. You can also use timeSpec() to find out if a QDateTime
- object stores a UTC time or a local time. Operations such as
- addSecs() and secsTo() are aware of daylight-saving time (DST).
+ milliseconds using addMSecs(), seconds using addSecs(), or days using
+ addDays(). Similarly, you can use addMonths() and addYears(). The daysTo()
+ function returns the number of days between two datetimes, secsTo() returns
+ the number of seconds between two datetimes, and msecsTo() returns the
+ number of milliseconds between two datetimes. These operations are aware of
+ daylight-saving time (DST) and other time-zone transitions, where
+ applicable.
+
+ Use toTimeSpec() to express a datetime in local time or UTC,
+ toOffsetFromUtc() to express in terms of an offset from UTC, or toTimeZone()
+ to express it with respect to a general time zone. You can use timeSpec() to
+ find out what time-spec a QDateTime object stores its time relative to. When
+ that is Qt::TimeZone, you can use timeZone() to find out which zone it is
+ using.
\note QDateTime does not account for leap seconds.
@@ -3316,67 +3333,55 @@ inline qint64 QDateTimePrivate::zoneMSecsToEpochMSecs(qint64 zoneMSecs, const QT
\section2 Range of Valid Dates
- The range of valid values able to be stored in QDateTime is dependent on
- the internal storage implementation. QDateTime is currently stored in a
- qint64 as a serial msecs value encoding the date and time. This restricts
- the date range to about +/- 292 million years, compared to the QDate range
- of +/- 2 billion years. Care must be taken when creating a QDateTime with
- extreme values that you do not overflow the storage. The exact range of
- supported values varies depending on the Qt::TimeSpec and time zone.
+ The range of values that QDateTime can represent is dependent on the
+ internal storage implementation. QDateTime is currently stored in a qint64
+ as a serial msecs value encoding the date and time. This restricts the date
+ range to about +/- 292 million years, compared to the QDate range of +/- 2
+ billion years. Care must be taken when creating a QDateTime with extreme
+ values that you do not overflow the storage. The exact range of supported
+ values varies depending on the Qt::TimeSpec and time zone.
- \section2 Use of System Timezone
+ \section2 Use of Timezones
- QDateTime uses the system's time zone information to determine the
- offset of local time from UTC. If the system is not configured
- correctly or not up-to-date, QDateTime will give wrong results as
- well.
+ QDateTime uses the system's time zone information to determine the current
+ local time zone and its offset from UTC. If the system is not configured
+ correctly or not up-to-date, QDateTime will give wrong results.
+
+ QDateTime likewise uses system-provided information to determine the offsets
+ of other timezones from UTC. If this information is incomplete or out of
+ date, QDateTime will give wrong results. See the QTimeZone documentation for
+ more details.
+
+ On modern Unix systems, this means QDateTime usually has accurate
+ information about historical transitions (including DST, see below) whenever
+ possible. On Windows, where the system doesn't support historical timezone
+ data, historical accuracy is not maintained with respect to timezone
+ transitions, notably including DST.
\section2 Daylight-Saving Time (DST)
- QDateTime takes into account the system's time zone information
- when dealing with DST. On modern Unix systems, this means it
- applies the correct historical DST data whenever possible. On
- Windows, where the system doesn't support historical DST data,
- historical accuracy is not maintained with respect to DST.
-
- The range of valid dates taking DST into account is 1970-01-01 to
- the present, and rules are in place for handling DST correctly
- until 2037-12-31, but these could change. For dates falling
- outside that range, QDateTime makes a \e{best guess} using the
- rules for year 1970 or 2037, but we can't guarantee accuracy. This
- means QDateTime doesn't take into account changes in a locale's
- time zone before 1970, even if the system's time zone database
- supports that information.
-
- QDateTime takes into consideration the Standard Time to Daylight-Saving Time
- transition. For example if the transition is at 2am and the clock goes
- forward to 3am, then there is a "missing" hour from 02:00:00 to 02:59:59.999
- which QDateTime considers to be invalid. Any date maths performed
- will take this missing hour into account and return a valid result.
-
- \section2 Offset From UTC
-
- A Qt::TimeSpec of Qt::OffsetFromUTC is also supported. This allows you
- to define a QDateTime relative to UTC at a fixed offset of a given number
- of seconds from UTC. For example, an offset of +3600 seconds is one hour
- ahead of UTC and is usually written in ISO standard notation as
- "UTC+01:00". Daylight-Saving Time never applies with this TimeSpec.
-
- There is no explicit size restriction to the offset seconds, but there is
- an implicit limit imposed when using the toString() and fromString()
- methods which use a format of [+|-]hh:mm, effectively limiting the range
- to +/- 99 hours and 59 minutes and whole minutes only. Note that currently
- no time zone lies outside the range of +/- 14 hours.
-
- \section2 Time Zone Support
-
- A Qt::TimeSpec of Qt::TimeZone is also supported in conjunction with the
- QTimeZone class. This allows you to define a datetime in a named time zone
- adhering to a consistent set of daylight-saving transition rules. For
- example a time zone of "Europe/Berlin" will apply the daylight-saving
- rules as used in Germany since 1970. Note that the transition rules
- applied depend on the platform support. See the QTimeZone documentation
- for more details.
+ QDateTime takes into account transitions between Standard Time and
+ Daylight-Saving Time. For example, if the transition is at 2am and the clock
+ goes forward to 3am, then there is a "missing" hour from 02:00:00 to
+ 02:59:59.999 which QDateTime considers to be invalid. Any date arithmetic
+ performed will take this missing hour into account and return a valid
+ result. For example, adding one minute to 01:59:59 will get 03:00:00.
+
+ The range of valid dates taking DST into account is 1970-01-01 to the
+ present, and rules are in place for handling DST correctly until 2037-12-31,
+ but these could change. For dates falling outside that range, QDateTime
+ makes a \e{best guess} using the rules for year 1970 or 2037, but we can't
+ guarantee accuracy. This means QDateTime doesn't take into account changes
+ in a time zone before 1970, even if the system's time zone database provides
+ that information.
+
+ \section2 Offsets From UTC
+
+ There is no explicit size restriction on an offset from UTC, but there is an
+ implicit limit imposed when using the toString() and fromString() methods
+ which use a [+|-]hh:mm format, effectively limiting the range to +/- 99
+ hours and 59 minutes and whole minutes only. Note that currently no time
+ zone lies outside the range of +/- 14 hours.
\sa QDate, QTime, QDateTimeEdit, QTimeZone
*/
@@ -3550,7 +3555,7 @@ QDate QDateTime::date() const
if (!status.testFlag(QDateTimePrivate::ValidDate))
return QDate();
QDate dt;
- msecsToTime(getMSecs(d), &dt, 0);
+ msecsToTime(getMSecs(d), &dt, nullptr);
return dt;
}
@@ -3566,7 +3571,7 @@ QTime QDateTime::time() const
if (!status.testFlag(QDateTimePrivate::ValidTime))
return QTime();
QTime tm;
- msecsToTime(getMSecs(d), 0, &tm);
+ msecsToTime(getMSecs(d), nullptr, &tm);
return tm;
}
@@ -3684,7 +3689,7 @@ QString QDateTime::timeZoneAbbreviation() const
case Qt::LocalTime: {
QString abbrev;
auto status = extractDaylightStatus(getStatus(d));
- localMSecsToEpochMSecs(getMSecs(d), &status, 0, 0, &abbrev);
+ localMSecsToEpochMSecs(getMSecs(d), &status, nullptr, nullptr, &abbrev);
return abbrev;
}
}
@@ -4499,7 +4504,7 @@ qint64 QDateTime::msecsTo(const QDateTime &other) const
Example:
\snippet code/src_corelib_tools_qdatetime.cpp 16
- \sa timeSpec(), toTimeZone(), toUTC(), toLocalTime()
+ \sa timeSpec(), toTimeZone(), toOffsetFromUtc()
*/
QDateTime QDateTime::toTimeSpec(Qt::TimeSpec spec) const
@@ -4769,14 +4774,14 @@ qint64 QDateTime::currentMSecsSinceEpoch() noexcept
// posix compliant system
// we have milliseconds
struct timeval tv;
- gettimeofday(&tv, 0);
+ gettimeofday(&tv, nullptr);
return qint64(tv.tv_sec) * Q_INT64_C(1000) + tv.tv_usec / 1000;
}
qint64 QDateTime::currentSecsSinceEpoch() noexcept
{
struct timeval tv;
- gettimeofday(&tv, 0);
+ gettimeofday(&tv, nullptr);
return qint64(tv.tv_sec);
}
#else
@@ -5098,48 +5103,45 @@ QDateTime QDateTime::fromString(const QString &string, Qt::DateFormat format)
return QDateTime();
// Accept "Sun Dec 1 13:02:00 1974" and "Sun 1. Dec 13:02:00 1974"
+
+ // Year and time can be in either order.
+ // Guess which by looking for ':' in the time
+ int yearPart = 3;
+ int timePart = 3;
+ if (parts.at(3).contains(QLatin1Char(':')))
+ yearPart = 4;
+ else if (parts.at(4).contains(QLatin1Char(':')))
+ timePart = 4;
+ else
+ return QDateTime();
+
int month = 0;
int day = 0;
bool ok = false;
- // First try month then day
+ int year = parts.at(yearPart).toInt(&ok);
+ if (!ok || year == 0)
+ return QDateTime();
+
+ // Next try month then day
month = fromShortMonthName(parts.at(1));
if (month)
- day = parts.at(2).toInt();
+ day = parts.at(2).toInt(&ok);
- // If failed try day then month
- if (!month || !day) {
+ // If failed, try day then month
+ if (!ok || !month || !day) {
month = fromShortMonthName(parts.at(2));
if (month) {
QStringRef dayStr = parts.at(1);
if (dayStr.endsWith(QLatin1Char('.'))) {
dayStr = dayStr.left(dayStr.size() - 1);
- day = dayStr.toInt();
+ day = dayStr.toInt(&ok);
}
}
}
// If both failed, give up
- if (!month || !day)
- return QDateTime();
-
- // Year can be before or after time, "Sun Dec 1 1974 13:02:00" or "Sun Dec 1 13:02:00 1974"
- // Guess which by looking for ':' in the time
- int year = 0;
- int yearPart = 0;
- int timePart = 0;
- if (parts.at(3).contains(QLatin1Char(':'))) {
- yearPart = 4;
- timePart = 3;
- } else if (parts.at(4).contains(QLatin1Char(':'))) {
- yearPart = 3;
- timePart = 4;
- } else {
- return QDateTime();
- }
-
- year = parts.at(yearPart).toInt(&ok);
- if (!ok)
+ if (!ok || !month || !day)
return QDateTime();
QDate date(year, month, day);
diff --git a/src/corelib/time/qtimezoneprivate_tz.cpp b/src/corelib/time/qtimezoneprivate_tz.cpp
index 2c845b1bce..f62d7998c8 100644
--- a/src/corelib/time/qtimezoneprivate_tz.cpp
+++ b/src/corelib/time/qtimezoneprivate_tz.cpp
@@ -50,6 +50,12 @@
#include <qdebug.h>
#include <algorithm>
+#include <errno.h>
+#include <limits.h>
+#if !defined(Q_OS_INTEGRITY)
+#include <sys/param.h> // to use MAXSYMLINKS constant
+#endif
+#include <unistd.h> // to use _SC_SYMLOOP_MAX constant
QT_BEGIN_NAMESPACE
@@ -1057,6 +1063,27 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::previousTransition(qint64 beforeMSecs
return last > m_tranTimes.cbegin() ? dataForTzTransition(*--last) : invalidData();
}
+static long getSymloopMax()
+{
+#if defined(SYMLOOP_MAX)
+ return SYMLOOP_MAX; // if defined, at runtime it can only be greater than this, so this is a safe bet
+#else
+ errno = 0;
+ long result = sysconf(_SC_SYMLOOP_MAX);
+ if (result >= 0)
+ return result;
+ // result is -1, meaning either error or no limit
+ Q_ASSERT(!errno); // ... but it can't be an error, POSIX mandates _SC_SYMLOOP_MAX
+
+ // therefore we can make up our own limit
+# if defined(MAXSYMLINKS)
+ return MAXSYMLINKS;
+# else
+ return 8;
+# endif
+#endif
+}
+
// TODO Could cache the value and monitor the required files for any changes
QByteArray QTzTimeZonePrivate::systemTimeZoneId() const
{
@@ -1074,12 +1101,18 @@ QByteArray QTzTimeZonePrivate::systemTimeZoneId() const
// On most distros /etc/localtime is a symlink to a real file so extract name from the path
if (ianaId.isEmpty()) {
- const QString path = QFile::symLinkTarget(QStringLiteral("/etc/localtime"));
- if (!path.isEmpty()) {
+ const QLatin1String zoneinfo("/zoneinfo/");
+ QString path = QFile::symLinkTarget(QStringLiteral("/etc/localtime"));
+ int index = -1;
+ long iteration = getSymloopMax();
+ // Symlink may point to another symlink etc. before being under zoneinfo/
+ // We stop on the first path under /zoneinfo/, even if it is itself a
+ // symlink, like America/Montreal pointing to America/Toronto
+ while (iteration-- > 0 && !path.isEmpty() && (index = path.indexOf(zoneinfo)) < 0)
+ path = QFile::symLinkTarget(path);
+ if (index >= 0) {
// /etc/localtime is a symlink to the current TZ file, so extract from path
- int index = path.indexOf(QLatin1String("/zoneinfo/"));
- if (index != -1)
- ianaId = path.mid(index + 10).toUtf8();
+ ianaId = path.midRef(index + zoneinfo.size()).toUtf8();
}
}
@@ -1105,9 +1138,9 @@ QByteArray QTzTimeZonePrivate::systemTimeZoneId() const
while (ianaId.isEmpty() && !ts.atEnd() && ts.status() == QTextStream::Ok) {
line = ts.readLine();
if (line.startsWith(QLatin1String("ZONE="))) {
- ianaId = line.mid(6, line.size() - 7).toUtf8();
+ ianaId = line.midRef(6, line.size() - 7).toUtf8();
} else if (line.startsWith(QLatin1String("TIMEZONE="))) {
- ianaId = line.mid(10, line.size() - 11).toUtf8();
+ ianaId = line.midRef(10, line.size() - 11).toUtf8();
}
}
}
diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp
index 88d8b8244d..234a44f6b6 100644
--- a/src/corelib/tools/qarraydata.cpp
+++ b/src/corelib/tools/qarraydata.cpp
@@ -215,7 +215,7 @@ QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment,
headerSize += (alignment - Q_ALIGNOF(QArrayData));
if (headerSize > size_t(MaxAllocSize))
- return 0;
+ return nullptr;
size_t allocSize = calculateBlockSize(capacity, objectSize, headerSize, options);
QArrayData *header = static_cast<QArrayData *>(::malloc(allocSize));
@@ -224,9 +224,9 @@ QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment,
& ~(alignment - 1);
#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
- header->ref.atomic.store(bool(!(options & Unsharable)));
+ header->ref.atomic.storeRelaxed(bool(!(options & Unsharable)));
#else
- header->ref.atomic.store(1);
+ header->ref.atomic.storeRelaxed(1);
#endif
header->size = 0;
header->alloc = capacity;
diff --git a/src/corelib/tools/qarraydata.h b/src/corelib/tools/qarraydata.h
index 7e3f8c9dbd..074072b987 100644
--- a/src/corelib/tools/qarraydata.h
+++ b/src/corelib/tools/qarraydata.h
@@ -324,10 +324,10 @@ struct QArrayDataPointerRef
/**/
#define Q_ARRAY_LITERAL_IMPL(Type, ...) \
- union { Type type_must_be_POD; } dummy; Q_UNUSED(dummy) \
+ Q_STATIC_ASSERT(std::is_literal_type<Type>::value); \
\
/* Portable compile-time array size computation */ \
- Type data[] = { __VA_ARGS__ }; Q_UNUSED(data) \
+ Q_CONSTEXPR Type data[] = { __VA_ARGS__ }; Q_UNUSED(data); \
enum { Size = sizeof(data) / sizeof(data[0]) }; \
\
static const QStaticArrayData<Type, Size> literal = { \
diff --git a/src/corelib/tools/qarraydataops.h b/src/corelib/tools/qarraydataops.h
index 7e1b43f9b1..8e19525f07 100644
--- a/src/corelib/tools/qarraydataops.h
+++ b/src/corelib/tools/qarraydataops.h
@@ -106,7 +106,7 @@ struct QPodArrayOps
void destroyAll() // Call from destructors, ONLY!
{
Q_ASSERT(this->isMutable());
- Q_ASSERT(this->ref.atomic.load() == 0);
+ Q_ASSERT(this->ref.atomic.loadRelaxed() == 0);
// As this is to be called only from destructor, it doesn't need to be
// exception safe; size not updated.
@@ -204,7 +204,7 @@ struct QGenericArrayOps
// As this is to be called only from destructor, it doesn't need to be
// exception safe; size not updated.
- Q_ASSERT(this->ref.atomic.load() == 0);
+ Q_ASSERT(this->ref.atomic.loadRelaxed() == 0);
const T *const b = this->begin();
const T *i = this->end();
diff --git a/src/corelib/tools/qbytearray.cpp b/src/corelib/tools/qbytearray.cpp
index 9526350126..c25d39461f 100644
--- a/src/corelib/tools/qbytearray.cpp
+++ b/src/corelib/tools/qbytearray.cpp
@@ -2072,7 +2072,7 @@ static inline QByteArray &qbytearray_insert(QByteArray *ba,
{
Q_ASSERT(pos >= 0);
- if (pos < 0 || len <= 0 || arr == 0)
+ if (pos < 0 || len <= 0 || arr == nullptr)
return *ba;
int oldsize = ba->size();
@@ -4775,7 +4775,7 @@ static void q_toPercentEncoding(QByteArray *ba, const char *dontEncode, const ch
QByteArray input = *ba;
int len = input.count();
const char *inputData = input.constData();
- char *output = 0;
+ char *output = nullptr;
int length = 0;
for (int i = 0; i < len; ++i) {
@@ -4815,7 +4815,7 @@ void q_toPercentEncoding(QByteArray *ba, const char *exclude, const char *includ
void q_normalizePercentEncoding(QByteArray *ba, const char *exclude)
{
q_fromPercentEncoding(ba, '%');
- q_toPercentEncoding(ba, exclude, 0, '%');
+ q_toPercentEncoding(ba, exclude, nullptr, '%');
}
/*!
diff --git a/src/corelib/tools/qbytearraymatcher.cpp b/src/corelib/tools/qbytearraymatcher.cpp
index d2eb4e0e3c..72e09226af 100644
--- a/src/corelib/tools/qbytearraymatcher.cpp
+++ b/src/corelib/tools/qbytearraymatcher.cpp
@@ -116,9 +116,9 @@ static inline int bm_find(const uchar *cc, int l, int index, const uchar *puc, u
Call setPattern() to give it a pattern to match.
*/
QByteArrayMatcher::QByteArrayMatcher()
- : d(0)
+ : d(nullptr)
{
- p.p = 0;
+ p.p = nullptr;
p.l = 0;
memset(p.q_skiptable, 0, sizeof(p.q_skiptable));
}
@@ -129,7 +129,7 @@ QByteArrayMatcher::QByteArrayMatcher()
the destructor does not delete \a pattern.
*/
QByteArrayMatcher::QByteArrayMatcher(const char *pattern, int length)
- : d(0)
+ : d(nullptr)
{
p.p = reinterpret_cast<const uchar *>(pattern);
p.l = length;
@@ -141,7 +141,7 @@ QByteArrayMatcher::QByteArrayMatcher(const char *pattern, int length)
Call indexIn() to perform a search.
*/
QByteArrayMatcher::QByteArrayMatcher(const QByteArray &pattern)
- : d(0), q_pattern(pattern)
+ : d(nullptr), q_pattern(pattern)
{
p.p = reinterpret_cast<const uchar *>(pattern.constData());
p.l = pattern.size();
@@ -152,7 +152,7 @@ QByteArrayMatcher::QByteArrayMatcher(const QByteArray &pattern)
Copies the \a other byte array matcher to this byte array matcher.
*/
QByteArrayMatcher::QByteArrayMatcher(const QByteArrayMatcher &other)
- : d(0)
+ : d(nullptr)
{
operator=(other);
}
diff --git a/src/corelib/tools/qchar.cpp b/src/corelib/tools/qchar.cpp
index d6061defc3..e097e4a5fe 100644
--- a/src/corelib/tools/qchar.cpp
+++ b/src/corelib/tools/qchar.cpp
@@ -1339,7 +1339,7 @@ static const unsigned short * QT_FASTCALL decompositionHelper
if (index == 0xffff) {
*length = 0;
*tag = QChar::NoDecomposition;
- return 0;
+ return nullptr;
}
const unsigned short *decomposition = uc_decomposition_map+index;
diff --git a/src/corelib/tools/qcollator.cpp b/src/corelib/tools/qcollator.cpp
index 6e85027462..958216bde8 100644
--- a/src/corelib/tools/qcollator.cpp
+++ b/src/corelib/tools/qcollator.cpp
@@ -166,7 +166,7 @@ QCollator &QCollator::operator=(const QCollator &other)
*/
void QCollator::detach()
{
- if (d->ref.load() != 1) {
+ if (d->ref.loadRelaxed() != 1) {
QCollatorPrivate *x = new QCollatorPrivate(d->locale);
if (!d->ref.deref())
delete d;
diff --git a/src/corelib/tools/qcontiguouscache.h b/src/corelib/tools/qcontiguouscache.h
index 592e31bfd2..7b74b4f526 100644
--- a/src/corelib/tools/qcontiguouscache.h
+++ b/src/corelib/tools/qcontiguouscache.h
@@ -100,8 +100,8 @@ public:
inline ~QContiguousCache() { if (!d) return; if (!d->ref.deref()) freeData(p); }
- inline void detach() { if (d->ref.load() != 1) detach_helper(); }
- inline bool isDetached() const { return d->ref.load() == 1; }
+ inline void detach() { if (d->ref.loadRelaxed() != 1) detach_helper(); }
+ inline bool isDetached() const { return d->ref.loadRelaxed() == 1; }
#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
inline void setSharable(bool sharable) { if (!sharable) detach(); d->sharable = sharable; }
#endif
@@ -176,7 +176,7 @@ void QContiguousCache<T>::detach_helper()
union { QContiguousCacheData *d; QContiguousCacheTypedData<T> *p; } x;
x.d = allocateData(d->alloc);
- x.d->ref.store(1);
+ x.d->ref.storeRelaxed(1);
x.d->count = d->count;
x.d->start = d->start;
x.d->offset = d->offset;
@@ -215,7 +215,7 @@ void QContiguousCache<T>::setCapacity(int asize)
detach();
union { QContiguousCacheData *d; QContiguousCacheTypedData<T> *p; } x;
x.d = allocateData(asize);
- x.d->ref.store(1);
+ x.d->ref.storeRelaxed(1);
x.d->alloc = asize;
x.d->count = qMin(d->count, asize);
x.d->offset = d->offset + d->count - x.d->count;
@@ -251,7 +251,7 @@ void QContiguousCache<T>::setCapacity(int asize)
template <typename T>
void QContiguousCache<T>::clear()
{
- if (d->ref.load() == 1) {
+ if (d->ref.loadRelaxed() == 1) {
if (QTypeInfo<T>::isComplex) {
int oldcount = d->count;
T * i = p->array + d->start;
@@ -267,7 +267,7 @@ void QContiguousCache<T>::clear()
} else {
union { QContiguousCacheData *d; QContiguousCacheTypedData<T> *p; } x;
x.d = allocateData(d->alloc);
- x.d->ref.store(1);
+ x.d->ref.storeRelaxed(1);
x.d->alloc = d->alloc;
x.d->count = x.d->start = x.d->offset = 0;
x.d->sharable = true;
@@ -287,7 +287,7 @@ QContiguousCache<T>::QContiguousCache(int cap)
{
Q_ASSERT(cap >= 0);
d = allocateData(cap);
- d->ref.store(1);
+ d->ref.storeRelaxed(1);
d->alloc = cap;
d->count = d->start = d->offset = 0;
d->sharable = true;
diff --git a/src/corelib/tools/qfreelist_p.h b/src/corelib/tools/qfreelist_p.h
index d72d6e1b4b..dcaf5688dc 100644
--- a/src/corelib/tools/qfreelist_p.h
+++ b/src/corelib/tools/qfreelist_p.h
@@ -171,7 +171,7 @@ class QFreeList
// qDebug("QFreeList: allocating %d elements (%ld bytes) with offset %d", size, size * sizeof(ElementType), offset);
ElementType *v = new ElementType[size];
for (int i = 0; i < size; ++i)
- v[i].next.store(offset + i + 1);
+ v[i].next.storeRelaxed(offset + i + 1);
return v;
}
@@ -218,21 +218,21 @@ template <typename T, typename ConstantsType>
inline QFreeList<T, ConstantsType>::~QFreeList()
{
for (int i = 0; i < ConstantsType::BlockCount; ++i)
- delete [] _v[i].load();
+ delete [] _v[i].loadRelaxed();
}
template <typename T, typename ConstantsType>
inline typename QFreeList<T, ConstantsType>::ConstReferenceType QFreeList<T, ConstantsType>::at(int x) const
{
const int block = blockfor(x);
- return (_v[block].load())[x].t();
+ return (_v[block].loadRelaxed())[x].t();
}
template <typename T, typename ConstantsType>
inline typename QFreeList<T, ConstantsType>::ReferenceType QFreeList<T, ConstantsType>::operator[](int x)
{
const int block = blockfor(x);
- return (_v[block].load())[x].t();
+ return (_v[block].loadRelaxed())[x].t();
}
template <typename T, typename ConstantsType>
@@ -257,7 +257,7 @@ inline int QFreeList<T, ConstantsType>::next()
}
}
- newid = v[at].next.load() | (id & ~ConstantsType::IndexMask);
+ newid = v[at].next.loadRelaxed() | (id & ~ConstantsType::IndexMask);
} while (!_next.testAndSetRelease(id, newid));
// qDebug("QFreeList::next(): returning %d (_next now %d, serial %d)",
// id & ConstantsType::IndexMask,
@@ -271,12 +271,12 @@ inline void QFreeList<T, ConstantsType>::release(int id)
{
int at = id & ConstantsType::IndexMask;
const int block = blockfor(at);
- ElementType *v = _v[block].load();
+ ElementType *v = _v[block].loadRelaxed();
int x, newid;
do {
x = _next.loadAcquire();
- v[at].next.store(x & ConstantsType::IndexMask);
+ v[at].next.storeRelaxed(x & ConstantsType::IndexMask);
newid = incrementserial(x, id);
} while (!_next.testAndSetRelease(x, newid));
diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp
index 85a3456d71..a53d6db997 100644
--- a/src/corelib/tools/qhash.cpp
+++ b/src/corelib/tools/qhash.cpp
@@ -321,7 +321,7 @@ static QBasicAtomicInt qt_qhash_seed = Q_BASIC_ATOMIC_INITIALIZER(-1);
*/
static void qt_initialize_qhash_seed()
{
- if (qt_qhash_seed.load() == -1) {
+ if (qt_qhash_seed.loadRelaxed() == -1) {
int x(qt_create_qhash_seed() & INT_MAX);
qt_qhash_seed.testAndSetRelaxed(-1, x);
}
@@ -340,7 +340,7 @@ static void qt_initialize_qhash_seed()
int qGlobalQHashSeed()
{
qt_initialize_qhash_seed();
- return qt_qhash_seed.load();
+ return qt_qhash_seed.loadRelaxed();
}
/*! \relates QHash
@@ -372,14 +372,14 @@ void qSetGlobalQHashSeed(int newSeed)
return;
if (newSeed == -1) {
int x(qt_create_qhash_seed() & INT_MAX);
- qt_qhash_seed.store(x);
+ qt_qhash_seed.storeRelaxed(x);
} else {
if (newSeed) {
// can't use qWarning here (reentrancy)
fprintf(stderr, "qSetGlobalQHashSeed: forced seed value is not 0, cannot guarantee that the "
"hashing functions will produce a stable value.");
}
- qt_qhash_seed.store(newSeed & INT_MAX);
+ qt_qhash_seed.storeRelaxed(newSeed & INT_MAX);
}
}
@@ -471,7 +471,7 @@ static int countBits(int hint)
const int MinNumBits = 4;
const QHashData QHashData::shared_null = {
- 0, 0, Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, MinNumBits, 0, 0, 0, true, false, 0
+ nullptr, nullptr, Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, MinNumBits, 0, 0, 0, true, false, 0
};
void *QHashData::allocateNode(int nodeAlign)
@@ -501,15 +501,15 @@ QHashData *QHashData::detach_helper(void (*node_duplicate)(Node *, void *),
if (this == &shared_null)
qt_initialize_qhash_seed(); // may throw
d = new QHashData;
- d->fakeNext = 0;
- d->buckets = 0;
+ d->fakeNext = nullptr;
+ d->buckets = nullptr;
d->ref.initializeOwned();
d->size = size;
d->nodeSize = nodeSize;
d->userNumBits = userNumBits;
d->numBits = numBits;
d->numBuckets = numBuckets;
- d->seed = (this == &shared_null) ? uint(qt_qhash_seed.load()) : seed;
+ d->seed = (this == &shared_null) ? uint(qt_qhash_seed.loadRelaxed()) : seed;
d->sharable = true;
d->strictAlignment = nodeAlign > 8;
d->reserved = 0;
diff --git a/src/corelib/tools/qlinkedlist.h b/src/corelib/tools/qlinkedlist.h
index 6bc053a4c0..d4e5bca0ed 100644
--- a/src/corelib/tools/qlinkedlist.h
+++ b/src/corelib/tools/qlinkedlist.h
@@ -340,7 +340,7 @@ void QLinkedList<T>::freeData(QLinkedListData *x)
{
Node *y = reinterpret_cast<Node*>(x);
Node *i = y->n;
- Q_ASSERT(x->ref.atomic.load() == 0);
+ Q_ASSERT(x->ref.atomic.loadRelaxed() == 0);
while (i != y) {
Node *n = i;
i = i->n;
diff --git a/src/corelib/tools/qlist.cpp b/src/corelib/tools/qlist.cpp
index 48617f0539..dfebd57e34 100644
--- a/src/corelib/tools/qlist.cpp
+++ b/src/corelib/tools/qlist.cpp
@@ -75,7 +75,7 @@ template class Q_CORE_EXPORT QVector<QPoint>;
the number of elements in the list.
*/
-const QListData::Data QListData::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, { 0 } };
+const QListData::Data QListData::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, { nullptr } };
/*!
* Detaches the QListData by allocating new memory for a list which will be bigger
diff --git a/src/corelib/tools/qlocale.cpp b/src/corelib/tools/qlocale.cpp
index c8740e55f3..939f8eb34d 100644
--- a/src/corelib/tools/qlocale.cpp
+++ b/src/corelib/tools/qlocale.cpp
@@ -301,9 +301,9 @@ QByteArray QLocaleId::name(char separator) const
const unsigned char *lang = language_code_list + 3 * language_id;
const unsigned char *script =
- (script_id != QLocale::AnyScript ? script_code_list + 4 * script_id : 0);
+ (script_id != QLocale::AnyScript ? script_code_list + 4 * script_id : nullptr);
const unsigned char *country =
- (country_id != QLocale::AnyCountry ? country_code_list + 3 * country_id : 0);
+ (country_id != QLocale::AnyCountry ? country_code_list + 3 * country_id : nullptr);
char len = (lang[2] != 0 ? 3 : 2) + (script ? 4+1 : 0) + (country ? (country[2] != 0 ? 3 : 2)+1 : 0);
QByteArray name(len, Qt::Uninitialized);
char *uc = name.data();
@@ -373,7 +373,7 @@ static const QLocaleData *findLocaleDataById(const QLocaleId &localeId)
} while (data->m_language_id && data->m_language_id == localeId.language_id);
}
- return 0;
+ return nullptr;
}
const QLocaleData *QLocaleData::findLocaleData(QLocale::Language language, QLocale::Script script, QLocale::Country country)
@@ -604,7 +604,7 @@ int qt_repeatCount(QStringView s)
return int(j);
}
-static const QLocaleData *default_data = 0;
+static const QLocaleData *default_data = nullptr;
static QLocale::NumberOptions default_number_options = QLocale::DefaultNumberOptions;
static const QLocaleData *const c_data = locale_data;
diff --git a/src/corelib/tools/qlocale_p.h b/src/corelib/tools/qlocale_p.h
index 15398ded32..1e3da35a02 100644
--- a/src/corelib/tools/qlocale_p.h
+++ b/src/corelib/tools/qlocale_p.h
@@ -250,14 +250,14 @@ public:
if (qIsInf(d))
return float(d);
if (std::fabs(d) > std::numeric_limits<float>::max()) {
- if (ok != nullptr)
+ if (ok)
*ok = false;
const float huge = std::numeric_limits<float>::infinity();
return d < 0 ? -huge : huge;
}
if (d != 0 && float(d) == 0) {
// Values that underflow double already failed. Match them:
- if (ok != 0)
+ if (ok)
*ok = false;
return 0;
}
@@ -337,7 +337,7 @@ public:
{
QLocalePrivate *retval = new QLocalePrivate;
retval->m_data = data;
- retval->ref.store(0);
+ retval->ref.storeRelaxed(0);
retval->m_numberOptions = numberOptions;
return retval;
}
diff --git a/src/corelib/tools/qlocale_tools.cpp b/src/corelib/tools/qlocale_tools.cpp
index 53258bec3e..db8c8cd12f 100644
--- a/src/corelib/tools/qlocale_tools.cpp
+++ b/src/corelib/tools/qlocale_tools.cpp
@@ -398,7 +398,7 @@ qstrtoull(const char * nptr, const char **endptr, int base, bool *ok)
*ok = true;
errno = 0;
- char *endptr2 = 0;
+ char *endptr2 = nullptr;
unsigned long long result = qt_strtoull(nptr, &endptr2, base);
if (endptr)
*endptr = endptr2;
@@ -415,7 +415,7 @@ qstrtoll(const char * nptr, const char **endptr, int base, bool *ok)
{
*ok = true;
errno = 0;
- char *endptr2 = 0;
+ char *endptr2 = nullptr;
long long result = qt_strtoll(nptr, &endptr2, base);
if (endptr)
*endptr = endptr2;
diff --git a/src/corelib/tools/qmap.cpp b/src/corelib/tools/qmap.cpp
index 5f7275c5f8..a0ec372f9a 100644
--- a/src/corelib/tools/qmap.cpp
+++ b/src/corelib/tools/qmap.cpp
@@ -48,7 +48,7 @@
QT_BEGIN_NAMESPACE
-const QMapDataBase QMapDataBase::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, { 0, 0, 0 }, 0 };
+const QMapDataBase QMapDataBase::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, { 0, nullptr, nullptr }, nullptr };
const QMapNodeBase *QMapNodeBase::nextNode() const
{
@@ -92,7 +92,7 @@ void QMapDataBase::rotateLeft(QMapNodeBase *x)
QMapNodeBase *&root = header.left;
QMapNodeBase *y = x->right;
x->right = y->left;
- if (y->left != 0)
+ if (y->left != nullptr)
y->left->setParent(x);
y->setParent(x->parent());
if (x == root)
@@ -111,7 +111,7 @@ void QMapDataBase::rotateRight(QMapNodeBase *x)
QMapNodeBase *&root = header.left;
QMapNodeBase *y = x->left;
x->left = y->right;
- if (y->right != 0)
+ if (y->right != nullptr)
y->right->setParent(x);
y->setParent(x->parent());
if (x == root)
@@ -173,7 +173,7 @@ void QMapDataBase::freeNodeAndRebalance(QMapNodeBase *z)
QMapNodeBase *y = z;
QMapNodeBase *x;
QMapNodeBase *x_parent;
- if (y->left == 0) {
+ if (y->left == nullptr) {
x = y->right;
if (y == mostLeftNode) {
if (x)
@@ -182,11 +182,11 @@ void QMapDataBase::freeNodeAndRebalance(QMapNodeBase *z)
mostLeftNode = y->parent();
}
} else {
- if (y->right == 0) {
+ if (y->right == nullptr) {
x = y->left;
} else {
y = y->right;
- while (y->left != 0)
+ while (y->left != nullptr)
y = y->left;
x = y->right;
}
@@ -228,7 +228,7 @@ void QMapDataBase::freeNodeAndRebalance(QMapNodeBase *z)
z->parent()->right = x;
}
if (y->color() != QMapNodeBase::Red) {
- while (x != root && (x == 0 || x->color() == QMapNodeBase::Black)) {
+ while (x != root && (x == nullptr || x->color() == QMapNodeBase::Black)) {
if (x == x_parent->left) {
QMapNodeBase *w = x_parent->right;
if (w->color() == QMapNodeBase::Red) {
@@ -237,13 +237,13 @@ void QMapDataBase::freeNodeAndRebalance(QMapNodeBase *z)
rotateLeft(x_parent);
w = x_parent->right;
}
- if ((w->left == 0 || w->left->color() == QMapNodeBase::Black) &&
- (w->right == 0 || w->right->color() == QMapNodeBase::Black)) {
+ if ((w->left == nullptr || w->left->color() == QMapNodeBase::Black) &&
+ (w->right == nullptr || w->right->color() == QMapNodeBase::Black)) {
w->setColor(QMapNodeBase::Red);
x = x_parent;
x_parent = x_parent->parent();
} else {
- if (w->right == 0 || w->right->color() == QMapNodeBase::Black) {
+ if (w->right == nullptr || w->right->color() == QMapNodeBase::Black) {
if (w->left)
w->left->setColor(QMapNodeBase::Black);
w->setColor(QMapNodeBase::Red);
@@ -265,13 +265,13 @@ void QMapDataBase::freeNodeAndRebalance(QMapNodeBase *z)
rotateRight(x_parent);
w = x_parent->left;
}
- if ((w->right == 0 || w->right->color() == QMapNodeBase::Black) &&
- (w->left == 0 || w->left->color() == QMapNodeBase::Black)) {
+ if ((w->right == nullptr || w->right->color() == QMapNodeBase::Black) &&
+ (w->left == nullptr|| w->left->color() == QMapNodeBase::Black)) {
w->setColor(QMapNodeBase::Red);
x = x_parent;
x_parent = x_parent->parent();
} else {
- if (w->left == 0 || w->left->color() == QMapNodeBase::Black) {
+ if (w->left == nullptr || w->left->color() == QMapNodeBase::Black) {
if (w->right)
w->right->setColor(QMapNodeBase::Black);
w->setColor(QMapNodeBase::Red);
@@ -363,8 +363,8 @@ QMapDataBase *QMapDataBase::createData()
d->size = 0;
d->header.p = 0;
- d->header.left = 0;
- d->header.right = 0;
+ d->header.left = nullptr;
+ d->header.right = nullptr;
d->mostLeftNode = &(d->header);
return d;
diff --git a/src/corelib/tools/qrefcount.h b/src/corelib/tools/qrefcount.h
index 71adb41f28..2e5388ad9a 100644
--- a/src/corelib/tools/qrefcount.h
+++ b/src/corelib/tools/qrefcount.h
@@ -52,7 +52,7 @@ class RefCount
{
public:
inline bool ref() noexcept {
- int count = atomic.load();
+ int count = atomic.loadRelaxed();
#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
if (count == 0) // !isSharable
return false;
@@ -63,7 +63,7 @@ public:
}
inline bool deref() noexcept {
- int count = atomic.load();
+ int count = atomic.loadRelaxed();
#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
if (count == 0) // !isSharable
return false;
@@ -86,24 +86,24 @@ public:
bool isSharable() const noexcept
{
// Sharable === Shared ownership.
- return atomic.load() != 0;
+ return atomic.loadRelaxed() != 0;
}
#endif
bool isStatic() const noexcept
{
// Persistent object, never deleted
- return atomic.load() == -1;
+ return atomic.loadRelaxed() == -1;
}
bool isShared() const noexcept
{
- int count = atomic.load();
+ int count = atomic.loadRelaxed();
return (count != 1) && (count != 0);
}
- void initializeOwned() noexcept { atomic.store(1); }
- void initializeUnsharable() noexcept { atomic.store(0); }
+ void initializeOwned() noexcept { atomic.storeRelaxed(1); }
+ void initializeUnsharable() noexcept { atomic.storeRelaxed(0); }
QBasicAtomicInt atomic;
};
diff --git a/src/corelib/tools/qregexp.cpp b/src/corelib/tools/qregexp.cpp
index d970843dea..128df84053 100644
--- a/src/corelib/tools/qregexp.cpp
+++ b/src/corelib/tools/qregexp.cpp
@@ -822,7 +822,7 @@ static QString wc2rx(const QString &wc_str, const bool enableEscaping)
if (wc[i] == QLatin1Char('^'))
rx += wc[i++];
if (i < wclen) {
- if (rx[i] == QLatin1Char(']'))
+ if (wc[i] == QLatin1Char(']'))
rx += wc[i++];
while (i < wclen && wc[i] != QLatin1Char(']')) {
if (wc[i] == QLatin1Char('\\'))
@@ -937,10 +937,10 @@ struct QRegExpMatchState
const QRegExpEngine *eng;
- inline QRegExpMatchState() : bigArray(0), captured(0) {}
+ inline QRegExpMatchState() : bigArray(nullptr), captured(nullptr) {}
inline ~QRegExpMatchState() { free(bigArray); }
- void drain() { free(bigArray); bigArray = 0; captured = 0; } // to save memory
+ void drain() { free(bigArray); bigArray = nullptr; captured = nullptr; } // to save memory
void prepareForMatch(QRegExpEngine *eng);
void match(const QChar *str, int len, int pos, bool minimal,
bool oneTest, int caretIndex);
@@ -1428,7 +1428,7 @@ void QRegExpMatchState::match(const QChar *str0, int len0, int pos0,
#endif
{
in = str0;
- if (in == 0)
+ if (in == nullptr)
in = &char_null;
pos = pos0;
caretPos = caretIndex;
@@ -1707,7 +1707,7 @@ void QRegExpEngine::dump() const
void QRegExpEngine::setup()
{
- ref.store(1);
+ ref.storeRelaxed(1);
#ifndef QT_NO_REGEXP_CAPTURE
f.resize(32);
nf = 0;
@@ -2910,7 +2910,7 @@ int QRegExpEngine::getEscape()
#ifndef QT_NO_REGEXP_ESCAPE
if ((prevCh & ~0xff) == 0) {
const char *p = strchr(tab, prevCh);
- if (p != 0)
+ if (p != nullptr)
return Tok_Char | backTab[p - tab];
}
#endif
@@ -3530,7 +3530,7 @@ int QRegExpEngine::parse(const QChar *pattern, int len)
#endif
box.cat(middleBox);
box.cat(rightBox);
- yyCharClass.reset(0);
+ yyCharClass.reset();
#ifndef QT_NO_REGEXP_CAPTURE
for (int i = 0; i < nf; ++i) {
@@ -3608,7 +3608,7 @@ int QRegExpEngine::parse(const QChar *pattern, int len)
void QRegExpEngine::parseAtom(Box *box)
{
#ifndef QT_NO_REGEXP_LOOKAHEAD
- QRegExpEngine *eng = 0;
+ QRegExpEngine *eng = nullptr;
bool neg;
int len;
#endif
@@ -3805,9 +3805,9 @@ struct QRegExpPrivate
QRegExpMatchState matchState;
inline QRegExpPrivate()
- : eng(0), engineKey(QString(), QRegExp::RegExp, Qt::CaseSensitive), minimal(false) { }
+ : eng(nullptr), engineKey(QString(), QRegExp::RegExp, Qt::CaseSensitive), minimal(false) { }
inline QRegExpPrivate(const QRegExpEngineKey &key)
- : eng(0), engineKey(key), minimal(false) {}
+ : eng(nullptr), engineKey(key), minimal(false) {}
};
#if !defined(QT_NO_REGEXP_OPTIM)
@@ -3886,9 +3886,9 @@ static void prepareEngineForMatch(QRegExpPrivate *priv, const QString &str)
static void invalidateEngine(QRegExpPrivate *priv)
{
- if (priv->eng != 0) {
+ if (priv->eng) {
derefEngine(priv->eng, priv->engineKey);
- priv->eng = 0;
+ priv->eng = nullptr;
priv->matchState.drain();
}
}
diff --git a/src/corelib/tools/qregularexpression.cpp b/src/corelib/tools/qregularexpression.cpp
index 9c201e770b..17acd476b2 100644
--- a/src/corelib/tools/qregularexpression.cpp
+++ b/src/corelib/tools/qregularexpression.cpp
@@ -460,34 +460,13 @@ QT_BEGIN_NAMESPACE
\row \li \c{"[a-z]+\\d+"} \li \b true \li \b true
\endtable
- Exact matching is not reflected in QRegularExpression. If you want to be
- sure that the subject string matches the regular expression exactly, you can wrap the
- pattern between a couple of anchoring expressions. Simply
- putting the pattern between the \c{^} and the \c{$} anchors is enough
- in most cases:
+ Exact matching is not reflected in QRegularExpression. If you want
+ to be sure that the subject string matches the regular expression
+ exactly, you can wrap the pattern using the anchoredPattern()
+ function:
\snippet code/src_corelib_tools_qregularexpression.cpp 24
- However, remember that the \c{$} anchor not only matches at the end of the
- string, but also at a newline character right before the end of the string;
- that is, the previous pattern matches against the string "this pattern must
- match exactly\\n". Also, the behaviour of both the \c{^} and the \c{$}
- anchors changes if the MultiLineOption is set either explicitly (as a
- pattern option) or implicitly (as a directive inside the pattern string).
-
- Therefore, in the most general case, you should wrap the pattern between
- the \c{\A} and the \c{\z} anchors:
-
- \snippet code/src_corelib_tools_qregularexpression.cpp 25
-
- Note the usage of the non-capturing group in order to preserve the meaning
- of the branch operator inside the pattern.
-
- The QRegularExpression::anchoredPattern() helper method does exactly that for
- you.
-
- \sa anchoredPattern
-
\section3 Porting from QRegExp's Partial Matching
When using QRegExp::exactMatch(), if an exact match was not found, one
diff --git a/src/corelib/tools/qringbuffer.cpp b/src/corelib/tools/qringbuffer.cpp
index 67cce57d01..311058a776 100644
--- a/src/corelib/tools/qringbuffer.cpp
+++ b/src/corelib/tools/qringbuffer.cpp
@@ -105,7 +105,7 @@ const char *QRingBuffer::readPointerAtPosition(qint64 pos, qint64 &length) const
}
length = 0;
- return 0;
+ return nullptr;
}
void QRingBuffer::free(qint64 bytes)
diff --git a/src/corelib/tools/qshareddata.h b/src/corelib/tools/qshareddata.h
index 816583c766..ab54c76720 100644
--- a/src/corelib/tools/qshareddata.h
+++ b/src/corelib/tools/qshareddata.h
@@ -75,7 +75,7 @@ public:
typedef T Type;
typedef T *pointer;
- inline void detach() { if (d && d->ref.load() != 1) detach_helper(); }
+ inline void detach() { if (d && d->ref.loadRelaxed() != 1) detach_helper(); }
inline T &operator*() { detach(); return *d; }
inline const T &operator*() const { return *d; }
inline T *operator->() { detach(); return d; }
@@ -163,7 +163,7 @@ public:
inline const T *constData() const { return d; }
inline T *take() { T *x = d; d = nullptr; return x; }
- inline void detach() { if (d && d->ref.load() != 1) detach_helper(); }
+ inline void detach() { if (d && d->ref.loadRelaxed() != 1) detach_helper(); }
inline void reset()
{
diff --git a/src/corelib/tools/qsharedpointer.cpp b/src/corelib/tools/qsharedpointer.cpp
index e4de5a2ba9..f185d2f23f 100644
--- a/src/corelib/tools/qsharedpointer.cpp
+++ b/src/corelib/tools/qsharedpointer.cpp
@@ -1380,7 +1380,7 @@ void QtSharedPointer::ExternalRefCountData::setQObjectShared(const QObject *, bo
*/
void QtSharedPointer::ExternalRefCountData::checkQObjectShared(const QObject *)
{
- if (strongref.load() < 0)
+ if (strongref.loadRelaxed() < 0)
qWarning("QSharedPointer: cannot create a QSharedPointer from a QObject-tracking QWeakPointer");
}
@@ -1390,7 +1390,7 @@ QtSharedPointer::ExternalRefCountData *QtSharedPointer::ExternalRefCountData::ge
QObjectPrivate *d = QObjectPrivate::get(const_cast<QObject *>(obj));
Q_ASSERT_X(!d->wasDeleted, "QWeakPointer", "Detected QWeakPointer creation in a QObject being deleted");
- ExternalRefCountData *that = d->sharedRefcount.load();
+ ExternalRefCountData *that = d->sharedRefcount.loadRelaxed();
if (that) {
that->weakref.ref();
return that;
@@ -1398,8 +1398,8 @@ QtSharedPointer::ExternalRefCountData *QtSharedPointer::ExternalRefCountData::ge
// we can create the refcount data because it doesn't exist
ExternalRefCountData *x = new ExternalRefCountData(Qt::Uninitialized);
- x->strongref.store(-1);
- x->weakref.store(2); // the QWeakPointer that called us plus the QObject itself
+ x->strongref.storeRelaxed(-1);
+ x->weakref.storeRelaxed(2); // the QWeakPointer that called us plus the QObject itself
ExternalRefCountData *ret;
if (d->sharedRefcount.testAndSetOrdered(nullptr, x, ret)) { // ought to be release+acquire; this is acq_rel+acquire
@@ -1407,7 +1407,7 @@ QtSharedPointer::ExternalRefCountData *QtSharedPointer::ExternalRefCountData::ge
} else {
// ~ExternalRefCountData has a Q_ASSERT, so we use this trick to
// only execute this if Q_ASSERTs are enabled
- Q_ASSERT((x->weakref.store(0), true));
+ Q_ASSERT((x->weakref.storeRelaxed(0), true));
delete x;
ret->weakref.ref();
}
diff --git a/src/corelib/tools/qsharedpointer_impl.h b/src/corelib/tools/qsharedpointer_impl.h
index 55f0f17c52..198cc58c38 100644
--- a/src/corelib/tools/qsharedpointer_impl.h
+++ b/src/corelib/tools/qsharedpointer_impl.h
@@ -107,6 +107,10 @@ template <class X, class T>
QSharedPointer<X> qSharedPointerObjectCast(const QSharedPointer<T> &ptr);
#endif
+namespace QtPrivate {
+struct EnableInternalData;
+}
+
namespace QtSharedPointer {
template <class T> class ExternalRefCount;
@@ -150,11 +154,11 @@ namespace QtSharedPointer {
inline ExternalRefCountData(DestroyerFn d)
: destroyer(d)
{
- strongref.store(1);
- weakref.store(1);
+ strongref.storeRelaxed(1);
+ weakref.storeRelaxed(1);
}
inline ExternalRefCountData(Qt::Initialization) { }
- ~ExternalRefCountData() { Q_ASSERT(!weakref.load()); Q_ASSERT(strongref.load() <= 0); }
+ ~ExternalRefCountData() { Q_ASSERT(!weakref.loadRelaxed()); Q_ASSERT(strongref.loadRelaxed() <= 0); }
void destroy() { destroyer(this); }
@@ -518,12 +522,12 @@ public:
if (o) {
// increase the strongref, but never up from zero
// or less (-1 is used by QWeakPointer on untracked QObject)
- int tmp = o->strongref.load();
+ int tmp = o->strongref.loadRelaxed();
while (tmp > 0) {
// try to increment from "tmp" to "tmp + 1"
if (o->strongref.testAndSetRelaxed(tmp, tmp + 1))
break; // succeeded
- tmp = o->strongref.load(); // failed, try again
+ tmp = o->strongref.loadRelaxed(); // failed, try again
}
if (tmp > 0) {
@@ -536,7 +540,7 @@ public:
qSwap(d, o);
qSwap(this->value, actual);
- if (!d || d->strongref.load() == 0)
+ if (!d || d->strongref.loadRelaxed() == 0)
this->value = nullptr;
// dereference saved data
@@ -562,7 +566,7 @@ public:
typedef const value_type &const_reference;
typedef qptrdiff difference_type;
- bool isNull() const noexcept { return d == nullptr || d->strongref.load() == 0 || value == nullptr; }
+ bool isNull() const noexcept { return d == nullptr || d->strongref.loadRelaxed() == 0 || value == nullptr; }
operator RestrictedBool() const noexcept { return isNull() ? nullptr : &QWeakPointer::value; }
bool operator !() const noexcept { return isNull(); }
@@ -672,21 +676,12 @@ public:
#endif
private:
-
+ friend struct QtPrivate::EnableInternalData;
#if defined(Q_NO_TEMPLATE_FRIENDS)
public:
#else
template <class X> friend class QSharedPointer;
template <class X> friend class QPointer;
-# ifndef QT_NO_QOBJECT
- template<typename X>
- friend QWeakPointer<typename std::enable_if<QtPrivate::IsPointerToTypeDerivedFromQObject<X*>::Value, X>::type>
- qWeakPointerFromVariant(const QVariant &variant);
-# endif
- template<typename X>
- friend QPointer<X>
- qPointerFromVariant(const QVariant &variant);
- friend QtPrivate::QSmartPointerConvertFunctor<QWeakPointer>;
#endif
template <class X>
@@ -714,13 +709,24 @@ public:
// a weak pointer's data but the weak pointer itself
inline T *internalData() const noexcept
{
- return d == nullptr || d->strongref.load() == 0 ? nullptr : value;
+ return d == nullptr || d->strongref.loadRelaxed() == 0 ? nullptr : value;
}
Data *d;
T *value;
};
+namespace QtPrivate {
+struct EnableInternalData {
+ template <typename T>
+ static T *internalData(const QWeakPointer<T> &p) noexcept { return p.internalData(); }
+};
+// hack to delay name lookup to instantiation time by making
+// EnableInternalData a dependent name:
+template <typename T>
+struct EnableInternalDataWrap : EnableInternalData {};
+}
+
template <class T>
class QEnableSharedFromThis
{
@@ -996,7 +1002,7 @@ template<typename T>
QWeakPointer<typename std::enable_if<QtPrivate::IsPointerToTypeDerivedFromQObject<T*>::Value, T>::type>
qWeakPointerFromVariant(const QVariant &variant)
{
- return QWeakPointer<T>(qobject_cast<T*>(QtSharedPointer::weakPointerFromVariant_internal(variant).internalData()));
+ return QWeakPointer<T>(qobject_cast<T*>(QtPrivate::EnableInternalData::internalData(QtSharedPointer::weakPointerFromVariant_internal(variant))));
}
template<typename T>
QSharedPointer<typename std::enable_if<QtPrivate::IsPointerToTypeDerivedFromQObject<T*>::Value, T>::type>
diff --git a/src/corelib/tools/qsimd.cpp b/src/corelib/tools/qsimd.cpp
index ddd715f745..ecf1822e42 100644
--- a/src/corelib/tools/qsimd.cpp
+++ b/src/corelib/tools/qsimd.cpp
@@ -563,9 +563,9 @@ quint64 qDetectCpuFeatures()
features_string + features_indices[qCountTrailingZeroBits(missing)]);
}
- qt_cpu_features[0].store(f | quint32(QSimdInitialized));
+ qt_cpu_features[0].storeRelaxed(f | quint32(QSimdInitialized));
#ifndef Q_ATOMIC_INT64_IS_SUPPORTED
- qt_cpu_features[1].store(f >> 32);
+ qt_cpu_features[1].storeRelaxed(f >> 32);
#endif
return f;
}
diff --git a/src/corelib/tools/qsimd_p.h b/src/corelib/tools/qsimd_p.h
index c36e1e484f..db2f546651 100644
--- a/src/corelib/tools/qsimd_p.h
+++ b/src/corelib/tools/qsimd_p.h
@@ -348,9 +348,9 @@ Q_CORE_EXPORT quint64 qDetectCpuFeatures();
static inline quint64 qCpuFeatures()
{
- quint64 features = qt_cpu_features[0].load();
+ quint64 features = qt_cpu_features[0].loadRelaxed();
#ifndef Q_ATOMIC_INT64_IS_SUPPORTED
- features |= quint64(qt_cpu_features[1].load()) << 32;
+ features |= quint64(qt_cpu_features[1].loadRelaxed()) << 32;
#endif
if (Q_UNLIKELY(features == 0)) {
features = qDetectCpuFeatures();
diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp
index 963c2a4d34..f9a9fcfe91 100644
--- a/src/corelib/tools/qstring.cpp
+++ b/src/corelib/tools/qstring.cpp
@@ -145,7 +145,8 @@ extern "C" void qt_toLatin1_mips_dsp_asm(uchar *dst, const ushort *src, int leng
// internal
qsizetype qFindStringBoyerMoore(QStringView haystack, qsizetype from, QStringView needle, Qt::CaseSensitivity cs);
static inline qsizetype qFindChar(QStringView str, QChar ch, qsizetype from, Qt::CaseSensitivity cs) noexcept;
-static inline qsizetype qt_last_index_of(QStringView haystack, QChar needle, qsizetype from, Qt::CaseSensitivity cs);
+template <typename Haystack>
+static inline qsizetype qLastIndexOf(Haystack haystack, QChar needle, qsizetype from, Qt::CaseSensitivity cs) noexcept;
static inline qsizetype qt_string_count(QStringView haystack, QStringView needle, Qt::CaseSensitivity cs);
static inline qsizetype qt_string_count(QStringView haystack, QChar needle, Qt::CaseSensitivity cs);
@@ -3044,7 +3045,7 @@ void QString::replace_helper(uint *indices, int nIndices, int blen, const QChar
{
// Copy after if it lies inside our own d->data() area (which we could
// possibly invalidate via a realloc or modify by replacement).
- QChar *afterBuffer = 0;
+ QChar *afterBuffer = nullptr;
if (pointsIntoRange(after, d->data(), d->size)) // Use copy in place of vulnerable original:
after = afterBuffer = textCopy(after, alen);
@@ -3129,7 +3130,7 @@ QString &QString::replace(const QChar *before, int blen,
return *this;
QStringMatcher matcher(before, blen, cs);
- QChar *beforeBuffer = 0, *afterBuffer = 0;
+ QChar *beforeBuffer = nullptr, *afterBuffer = nullptr;
int index = 0;
while (1) {
@@ -3779,74 +3780,6 @@ int QString::indexOf(const QStringRef &str, int from, Qt::CaseSensitivity cs) co
// ### Qt6: qsize
return int(QtPrivate::findString(QStringView(unicode(), length()), from, QStringView(str.unicode(), str.length()), cs));
}
-#endif // QT_STRINGVIEW_LEVEL < 2
-
-static int lastIndexOfHelper(const ushort *haystack, int from, const ushort *needle, int sl, Qt::CaseSensitivity cs)
-{
- /*
- See indexOf() for explanations.
- */
-
- auto sv = [sl](const ushort *v) { return QStringView(v, sl); };
-
- const ushort *end = haystack;
- haystack += from;
- const uint sl_minus_1 = sl - 1;
- const ushort *n = needle+sl_minus_1;
- const ushort *h = haystack+sl_minus_1;
- uint hashNeedle = 0, hashHaystack = 0;
- int idx;
-
- if (cs == Qt::CaseSensitive) {
- for (idx = 0; idx < sl; ++idx) {
- hashNeedle = ((hashNeedle<<1) + *(n-idx));
- hashHaystack = ((hashHaystack<<1) + *(h-idx));
- }
- hashHaystack -= *haystack;
-
- while (haystack >= end) {
- hashHaystack += *haystack;
- if (hashHaystack == hashNeedle
- && qt_compare_strings(sv(needle), sv(haystack), Qt::CaseSensitive) == 0)
- return haystack - end;
- --haystack;
- REHASH(haystack[sl]);
- }
- } else {
- for (idx = 0; idx < sl; ++idx) {
- hashNeedle = ((hashNeedle<<1) + foldCase(n-idx, needle));
- hashHaystack = ((hashHaystack<<1) + foldCase(h-idx, end));
- }
- hashHaystack -= foldCase(haystack, end);
-
- while (haystack >= end) {
- hashHaystack += foldCase(haystack, end);
- if (hashHaystack == hashNeedle
- && qt_compare_strings(sv(haystack), sv(needle), Qt::CaseInsensitive) == 0)
- return haystack - end;
- --haystack;
- REHASH(foldCase(haystack + sl, end));
- }
- }
- return -1;
-}
-
-static inline int lastIndexOfHelper(
- const QStringRef &haystack, int from, const QStringRef &needle, Qt::CaseSensitivity cs)
-{
- return lastIndexOfHelper(reinterpret_cast<const ushort*>(haystack.unicode()), from,
- reinterpret_cast<const ushort*>(needle.unicode()), needle.size(), cs);
-}
-
-static inline int lastIndexOfHelper(
- const QStringRef &haystack, int from, QLatin1String needle, Qt::CaseSensitivity cs)
-{
- const int size = needle.size();
- QVarLengthArray<ushort> s(size);
- qt_from_latin1(s.data(), needle.latin1(), size);
- return lastIndexOfHelper(reinterpret_cast<const ushort*>(haystack.unicode()), from,
- s.data(), size, cs);
-}
/*!
Returns the index position of the last occurrence of the string \a
@@ -3866,9 +3799,12 @@ static inline int lastIndexOfHelper(
*/
int QString::lastIndexOf(const QString &str, int from, Qt::CaseSensitivity cs) const
{
- return QStringRef(this).lastIndexOf(QStringRef(&str), from, cs);
+ // ### Qt6: qsize
+ return int(QtPrivate::lastIndexOf(*this, from, str, cs));
}
+#endif // QT_STRINGVIEW_LEVEL < 2
+
/*!
\since 4.5
\overload lastIndexOf()
@@ -3890,7 +3826,8 @@ int QString::lastIndexOf(const QString &str, int from, Qt::CaseSensitivity cs) c
*/
int QString::lastIndexOf(QLatin1String str, int from, Qt::CaseSensitivity cs) const
{
- return QStringRef(this).lastIndexOf(str, from, cs);
+ // ### Qt6: qsize
+ return int(QtPrivate::lastIndexOf(*this, from, str, cs));
}
/*!
@@ -3902,9 +3839,10 @@ int QString::lastIndexOf(QLatin1String str, int from, Qt::CaseSensitivity cs) co
int QString::lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
{
// ### Qt6: qsize
- return int(qt_last_index_of(QStringView(unicode(), size()), ch, from, cs));
+ return int(qLastIndexOf(*this, ch, from, cs));
}
+#if QT_STRINGVIEW_LEVEL < 2
/*!
\since 4.8
\overload lastIndexOf()
@@ -3922,8 +3860,27 @@ int QString::lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
*/
int QString::lastIndexOf(const QStringRef &str, int from, Qt::CaseSensitivity cs) const
{
- return QStringRef(this).lastIndexOf(str, from, cs);
+ // ### Qt6: qsize
+ return int(QtPrivate::lastIndexOf(*this, from, str, cs));
}
+#endif // QT_STRINGVIEW_LEVEL < 2
+
+/*!
+ \fn int QString::lastIndexOf(QStringView str, int from, Qt::CaseSensitivity cs) const
+ \since 5.14
+ \overload lastIndexOf()
+
+ Returns the index position of the last occurrence of the string view \a
+ str in this string, searching backward from index position \a
+ from. If \a from is -1 (default), the search starts at the last
+ character; if \a from is -2, at the next to last character and so
+ on. Returns -1 if \a str is not found.
+
+ If \a cs is Qt::CaseSensitive (default), the search is case
+ sensitive; otherwise the search is case insensitive.
+
+ \sa indexOf(), contains(), count()
+*/
#if !(defined(QT_NO_REGEXP) && !QT_CONFIG(regularexpression))
@@ -5591,7 +5548,7 @@ QString QString::fromUtf16(const ushort *unicode, int size)
while (unicode[size] != 0)
++size;
}
- return QUtf16::convertToUnicode((const char *)unicode, size*2, 0);
+ return QUtf16::convertToUnicode((const char *)unicode, size*2, nullptr);
}
/*!
@@ -5645,7 +5602,7 @@ QString QString::fromUcs4(const uint *unicode, int size)
while (unicode[size] != 0)
++size;
}
- return QUtf32::convertToUnicode((const char *)unicode, size*4, 0);
+ return QUtf32::convertToUnicode((const char *)unicode, size*4, nullptr);
}
@@ -8060,7 +8017,7 @@ void qt_string_normalize(QString *data, QString::NormalizationForm mode, QChar::
version = QChar::currentUnicodeVersion();
} else if (int(version) <= NormalizationCorrectionsVersionMax) {
const QString &s = *data;
- QChar *d = 0;
+ QChar *d = nullptr;
for (int i = 0; i < NumNormalizationCorrections; ++i) {
const NormalizationCorrection &n = uc_normalization_corrections[i];
if (n.version > version) {
@@ -8797,19 +8754,23 @@ QString QString::arg(double a, int fieldWidth, char fmt, int prec, QChar fillCha
return replaceArgEscapes(*this, d, fieldWidth, arg, locale_arg, fillChar);
}
-static int getEscape(const QChar *uc, int *pos, int len, int maxNumber = 999)
+static inline ushort to_unicode(const QChar c) { return c.unicode(); }
+static inline ushort to_unicode(const char c) { return QLatin1Char{c}.unicode(); }
+
+template <typename Char>
+static int getEscape(const Char *uc, qsizetype *pos, qsizetype len, int maxNumber = 999)
{
int i = *pos;
++i;
if (i < len && uc[i] == QLatin1Char('L'))
++i;
if (i < len) {
- int escape = uc[i].unicode() - '0';
+ int escape = to_unicode(uc[i]) - '0';
if (uint(escape) >= 10U)
return -1;
++i;
while (i < len) {
- int digit = uc[i].unicode() - '0';
+ int digit = to_unicode(uc[i]) - '0';
if (uint(digit) >= 10U)
break;
escape = (escape * 10) + digit;
@@ -8860,18 +8821,23 @@ static int getEscape(const QChar *uc, int *pos, int len, int maxNumber = 999)
namespace {
struct Part
{
- Part() : stringRef(), number(0) {}
- Part(const QString &s, int pos, int len, int num = -1) noexcept
- : stringRef(&s, pos, len), number(num) {}
+ Part() = default; // for QVarLengthArray; do not use
+ Q_DECL_CONSTEXPR Part(QStringView s, int num = -1)
+ : tag{QtPrivate::ArgBase::U16}, number{num}, data{s.utf16()}, size{s.size()} {}
+ Q_DECL_CONSTEXPR Part(QLatin1String s, int num = -1)
+ : tag{QtPrivate::ArgBase::L1}, number{num}, data{s.data()}, size{s.size()} {}
+
+ void reset(QStringView s) noexcept { *this = {s, number}; }
+ void reset(QLatin1String s) noexcept { *this = {s, number}; }
- QStringRef stringRef;
+ QtPrivate::ArgBase::Tag tag;
int number;
+ const void *data;
+ qsizetype size;
};
} // unnamed namespace
-template <>
-class QTypeInfo<Part> : public QTypeInfoMerger<Part, QStringRef, int> {}; // Q_DECLARE_METATYPE
-
+Q_DECLARE_TYPEINFO(Part, Q_PRIMITIVE_TYPE);
namespace {
@@ -8880,24 +8846,25 @@ enum { ExpectedParts = 32 };
typedef QVarLengthArray<Part, ExpectedParts> ParseResult;
typedef QVarLengthArray<int, ExpectedParts/2> ArgIndexToPlaceholderMap;
-static ParseResult parseMultiArgFormatString(const QString &s)
+template <typename StringView>
+static ParseResult parseMultiArgFormatString(StringView s)
{
ParseResult result;
- const QChar *uc = s.constData();
- const int len = s.size();
- const int end = len - 1;
- int i = 0;
- int last = 0;
+ const auto uc = s.data();
+ const auto len = s.size();
+ const auto end = len - 1;
+ qsizetype i = 0;
+ qsizetype last = 0;
while (i < end) {
if (uc[i] == QLatin1Char('%')) {
- int percent = i;
+ qsizetype percent = i;
int number = getEscape(uc, &i, len);
if (number != -1) {
if (last != percent)
- result.push_back(Part(s, last, percent - last)); // literal text (incl. failed placeholders)
- result.push_back(Part(s, percent, i - percent, number)); // parsed placeholder
+ result.push_back(Part{s.mid(last, percent - last)}); // literal text (incl. failed placeholders)
+ result.push_back(Part{s.mid(percent, i - percent), number}); // parsed placeholder
last = i;
continue;
}
@@ -8906,7 +8873,7 @@ static ParseResult parseMultiArgFormatString(const QString &s)
}
if (last < len)
- result.push_back(Part(s, last, len - last)); // trailing literal text
+ result.push_back(Part{s.mid(last, len - last)}); // trailing literal text
return result;
}
@@ -8915,9 +8882,9 @@ static ArgIndexToPlaceholderMap makeArgIndexToPlaceholderMap(const ParseResult &
{
ArgIndexToPlaceholderMap result;
- for (ParseResult::const_iterator it = parts.begin(), end = parts.end(); it != end; ++it) {
- if (it->number >= 0)
- result.push_back(it->number);
+ for (Part part : parts) {
+ if (part.number >= 0)
+ result.push_back(part.number);
}
std::sort(result.begin(), result.end());
@@ -8927,54 +8894,107 @@ static ArgIndexToPlaceholderMap makeArgIndexToPlaceholderMap(const ParseResult &
return result;
}
-static int resolveStringRefsAndReturnTotalSize(ParseResult &parts, const ArgIndexToPlaceholderMap &argIndexToPlaceholderMap, const QString *args[])
+static qsizetype resolveStringRefsAndReturnTotalSize(ParseResult &parts, const ArgIndexToPlaceholderMap &argIndexToPlaceholderMap, const QtPrivate::ArgBase *args[])
{
- int totalSize = 0;
- for (ParseResult::iterator pit = parts.begin(), end = parts.end(); pit != end; ++pit) {
- if (pit->number != -1) {
- const ArgIndexToPlaceholderMap::const_iterator ait
- = std::find(argIndexToPlaceholderMap.begin(), argIndexToPlaceholderMap.end(), pit->number);
- if (ait != argIndexToPlaceholderMap.end())
- pit->stringRef = QStringRef(args[ait - argIndexToPlaceholderMap.begin()]);
+ using namespace QtPrivate;
+ qsizetype totalSize = 0;
+ for (Part &part : parts) {
+ if (part.number != -1) {
+ const auto it = std::find(argIndexToPlaceholderMap.begin(), argIndexToPlaceholderMap.end(), part.number);
+ if (it != argIndexToPlaceholderMap.end()) {
+ const auto &arg = *args[it - argIndexToPlaceholderMap.begin()];
+ switch (arg.tag) {
+ case ArgBase::L1:
+ part.reset(static_cast<const QLatin1StringArg&>(arg).string);
+ break;
+ case ArgBase::U8:
+ Q_UNREACHABLE(); // waiting for QUtf8String...
+ break;
+ case ArgBase::U16:
+ part.reset(static_cast<const QStringViewArg&>(arg).string);
+ break;
+ }
+ }
}
- totalSize += pit->stringRef.size();
+ totalSize += part.size;
}
return totalSize;
}
} // unnamed namespace
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QString QString::multiArg(int numArgs, const QString **args) const
{
+ QVarLengthArray<QtPrivate::QStringViewArg, 9> sva;
+ sva.reserve(numArgs);
+ QVarLengthArray<const QtPrivate::ArgBase *, 9> pointers;
+ pointers.reserve(numArgs);
+ for (int i = 0; i < numArgs; ++i) {
+ sva.push_back(QtPrivate::qStringLikeToArg(*args[i]));
+ pointers.push_back(&sva.back());
+ }
+ return QtPrivate::argToQString(qToStringViewIgnoringNull(*this), static_cast<size_t>(numArgs), pointers.data());
+}
+#endif
+
+Q_ALWAYS_INLINE QString to_string(QLatin1String s) noexcept { return s; }
+Q_ALWAYS_INLINE QString to_string(QStringView s) noexcept { return s.toString(); }
+
+template <typename StringView>
+static QString argToQStringImpl(StringView pattern, size_t numArgs, const QtPrivate::ArgBase **args)
+{
// Step 1-2 above
- ParseResult parts = parseMultiArgFormatString(*this);
+ ParseResult parts = parseMultiArgFormatString(pattern);
// 3-4
ArgIndexToPlaceholderMap argIndexToPlaceholderMap = makeArgIndexToPlaceholderMap(parts);
- if (argIndexToPlaceholderMap.size() > numArgs) // 3a
- argIndexToPlaceholderMap.resize(numArgs);
- else if (argIndexToPlaceholderMap.size() < numArgs) // 3b
- qWarning("QString::arg: %d argument(s) missing in %s",
- numArgs - argIndexToPlaceholderMap.size(), toLocal8Bit().data());
+ if (static_cast<size_t>(argIndexToPlaceholderMap.size()) > numArgs) // 3a
+ argIndexToPlaceholderMap.resize(int(numArgs));
+ else if (Q_UNLIKELY(static_cast<size_t>(argIndexToPlaceholderMap.size()) < numArgs)) // 3b
+ qWarning("QString::arg: %d argument(s) missing in %ls",
+ int(numArgs - argIndexToPlaceholderMap.size()), qUtf16Printable(to_string(pattern)));
// 5
- const int totalSize = resolveStringRefsAndReturnTotalSize(parts, argIndexToPlaceholderMap, args);
+ const qsizetype totalSize = resolveStringRefsAndReturnTotalSize(parts, argIndexToPlaceholderMap, args);
// 6:
QString result(totalSize, Qt::Uninitialized);
- QChar *out = result.data();
-
- for (ParseResult::const_iterator it = parts.begin(), end = parts.end(); it != end; ++it) {
- if (const int sz = it->stringRef.size()) {
- memcpy(out, it->stringRef.constData(), sz * sizeof(QChar));
- out += sz;
+ auto out = const_cast<QChar*>(result.constData());
+
+ for (Part part : parts) {
+ switch (part.tag) {
+ case QtPrivate::ArgBase::L1:
+ if (part.size) {
+ qt_from_latin1(reinterpret_cast<ushort*>(out),
+ reinterpret_cast<const char*>(part.data), part.size);
+ }
+ break;
+ case QtPrivate::ArgBase::U8:
+ Q_UNREACHABLE(); // waiting for QUtf8String
+ break;
+ case QtPrivate::ArgBase::U16:
+ if (part.size)
+ memcpy(out, part.data, part.size * sizeof(QChar));
+ break;
}
+ out += part.size;
}
return result;
}
+QString QtPrivate::argToQString(QStringView pattern, size_t n, const ArgBase **args)
+{
+ return argToQStringImpl(pattern, n, args);
+}
+
+QString QtPrivate::argToQString(QLatin1String pattern, size_t n, const ArgBase **args)
+{
+ return argToQStringImpl(pattern, n, args);
+}
+
/*! \fn bool QString::isSimpleText() const
\internal
@@ -9562,6 +9582,24 @@ QString &QString::setRawData(const QChar *unicode, int size)
*/
/*!
+ \fn int QLatin1String::lastIndexOf(QStringView str, int from, Qt::CaseSensitivity cs) const
+ \fn int QLatin1String::lastIndexOf(QLatin1String l1, int from, Qt::CaseSensitivity cs) const
+ \fn int QLatin1String::lastIndexOf(QChar c, int from, Qt::CaseSensitivity cs) const
+ \since 5.14
+
+ Returns the index position of the last occurrence of the string-view \a str,
+ Latin-1 string \a l1, or character \a ch, respectively, in this Latin-1 string,
+ searching backward from index position \a from. If \a from is -1 (default),
+ the search starts at the last character; if \a from is -2, at the next to last
+ character and so on. Returns -1 if \a str is not found.
+
+ If \a cs is Qt::CaseSensitive (default), the search is case
+ sensitive; otherwise the search is case insensitive.
+
+ \sa indexOf(), QStringView::lastIndexOf(), QStringView::indexOf(), QString::indexOf()
+*/
+
+/*!
\fn QLatin1String::const_iterator QLatin1String::begin() const
\since 5.10
@@ -11232,7 +11270,8 @@ int QStringRef::indexOf(const QStringRef &str, int from, Qt::CaseSensitivity cs)
*/
int QStringRef::lastIndexOf(const QString &str, int from, Qt::CaseSensitivity cs) const
{
- return lastIndexOf(QStringRef(&str), from, cs);
+ // ### Qt6: qsize
+ return int(QtPrivate::lastIndexOf(*this, from, str, cs));
}
/*!
@@ -11247,28 +11286,7 @@ int QStringRef::lastIndexOf(const QString &str, int from, Qt::CaseSensitivity cs
int QStringRef::lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
{
// ### Qt6: qsize
- return int(qt_last_index_of(QStringView(unicode(), size()), ch, from, cs));
-}
-
-template<typename T>
-static int last_index_of_impl(const QStringRef &haystack, int from, const T &needle, Qt::CaseSensitivity cs)
-{
- const int sl = needle.size();
- if (sl == 1)
- return haystack.lastIndexOf(needle.at(0), from, cs);
-
- const int l = haystack.size();
- if (from < 0)
- from += l;
- int delta = l - sl;
- if (from == l && sl == 0)
- return from;
- if (uint(from) >= uint(l) || delta < 0)
- return -1;
- if (from > delta)
- from = delta;
-
- return lastIndexOfHelper(haystack, from, needle, cs);
+ return int(qLastIndexOf(*this, ch, from, cs));
}
/*!
@@ -11288,7 +11306,8 @@ static int last_index_of_impl(const QStringRef &haystack, int from, const T &nee
*/
int QStringRef::lastIndexOf(QLatin1String str, int from, Qt::CaseSensitivity cs) const
{
- return last_index_of_impl(*this, from, str, cs);
+ // ### Qt6: qsize
+ return int(QtPrivate::lastIndexOf(*this, from, str, cs));
}
/*!
@@ -11308,10 +11327,28 @@ int QStringRef::lastIndexOf(QLatin1String str, int from, Qt::CaseSensitivity cs)
*/
int QStringRef::lastIndexOf(const QStringRef &str, int from, Qt::CaseSensitivity cs) const
{
- return last_index_of_impl(*this, from, str, cs);
+ // ### Qt6: qsize
+ return int(QtPrivate::lastIndexOf(*this, from, str, cs));
}
/*!
+ \fn int QStringRef::lastIndexOf(QStringView str, int from, Qt::CaseSensitivity cs) const
+ \since 5.14
+ \overload lastIndexOf()
+
+ Returns the index position of the last occurrence of the string view \a
+ str in this string, searching backward from index position \a
+ from. If \a from is -1 (default), the search starts at the last
+ character; if \a from is -2, at the next to last character and so
+ on. Returns -1 if \a str is not found.
+
+ If \a cs is Qt::CaseSensitive (default), the search is case
+ sensitive; otherwise the search is case insensitive.
+
+ \sa indexOf(), contains(), count()
+*/
+
+/*!
\since 4.8
Returns the number of (potentially overlapping) occurrences of
the string \a str in this string reference.
@@ -11613,33 +11650,6 @@ bool QStringRef::endsWith(const QStringRef &str, Qt::CaseSensitivity cs) const
\sa indexOf(), count()
*/
-static inline qsizetype qt_last_index_of(QStringView haystack, QChar needle,
- qsizetype from, Qt::CaseSensitivity cs)
-{
- if (from < 0)
- from += haystack.size();
- if (std::size_t(from) >= std::size_t(haystack.size()))
- return -1;
- if (from >= 0) {
- ushort c = needle.unicode();
- const ushort *b = reinterpret_cast<const ushort*>(haystack.data());
- const ushort *n = b + from;
- if (cs == Qt::CaseSensitive) {
- for (; n >= b; --n)
- if (*n == c)
- return n - b;
- } else {
- c = foldCase(c);
- for (; n >= b; --n)
- if (foldCase(*n) == c)
- return n - b;
- }
- }
- return -1;
-
-
-}
-
static inline qsizetype qt_string_count(QStringView haystack, QStringView needle, Qt::CaseSensitivity cs)
{
qsizetype num = 0;
@@ -11821,6 +11831,38 @@ bool QtPrivate::endsWith(QLatin1String haystack, QLatin1String needle, Qt::CaseS
return qt_ends_with_impl(haystack, needle, cs);
}
+namespace {
+template <typename Pointer>
+uint foldCaseHelper(Pointer ch, Pointer start) = delete;
+
+template <>
+uint foldCaseHelper<const QChar*>(const QChar* ch, const QChar* start)
+{
+ return foldCase(reinterpret_cast<const ushort*>(ch), reinterpret_cast<const ushort*>(start));
+}
+
+template <>
+uint foldCaseHelper<const char*>(const char* ch, const char*)
+{
+ return foldCase(ushort(uchar(*ch)));
+}
+
+template <typename T>
+ushort valueTypeToUtf16(T t) = delete;
+
+template <>
+ushort valueTypeToUtf16<QChar>(QChar t)
+{
+ return t.unicode();
+}
+
+template <>
+ushort valueTypeToUtf16<char>(char t)
+{
+ return ushort(uchar(t));
+}
+}
+
/*!
\internal
@@ -11929,6 +11971,97 @@ qsizetype QtPrivate::findString(QStringView haystack0, qsizetype from, QStringVi
return -1;
}
+template <typename Haystack>
+static inline qsizetype qLastIndexOf(Haystack haystack, QChar needle,
+ qsizetype from, Qt::CaseSensitivity cs) noexcept
+{
+ if (from < 0)
+ from += haystack.size();
+ if (std::size_t(from) >= std::size_t(haystack.size()))
+ return -1;
+ if (from >= 0) {
+ ushort c = needle.unicode();
+ const auto b = haystack.data();
+ auto n = b + from;
+ if (cs == Qt::CaseSensitive) {
+ for (; n >= b; --n)
+ if (valueTypeToUtf16(*n) == c)
+ return n - b;
+ } else {
+ c = foldCase(c);
+ for (; n >= b; --n)
+ if (foldCase(valueTypeToUtf16(*n)) == c)
+ return n - b;
+ }
+ }
+ return -1;
+}
+
+template<typename Haystack, typename Needle>
+static qsizetype qLastIndexOf(Haystack haystack0, qsizetype from,
+ Needle needle0, Qt::CaseSensitivity cs) noexcept
+{
+ const qsizetype sl = needle0.size();
+ if (sl == 1)
+ return qLastIndexOf(haystack0, needle0.front(), from, cs);
+
+ const qsizetype l = haystack0.size();
+ if (from < 0)
+ from += l;
+ if (from == l && sl == 0)
+ return from;
+ const qsizetype delta = l - sl;
+ if (std::size_t(from) >= std::size_t(l) || delta < 0)
+ return -1;
+ if (from > delta)
+ from = delta;
+
+ auto sv = [sl](const typename Haystack::value_type *v) { return Haystack(v, sl); };
+
+ auto haystack = haystack0.data();
+ const auto needle = needle0.data();
+ const auto *end = haystack;
+ haystack += from;
+ const std::size_t sl_minus_1 = sl - 1;
+ const auto *n = needle + sl_minus_1;
+ const auto *h = haystack + sl_minus_1;
+ std::size_t hashNeedle = 0, hashHaystack = 0;
+ qsizetype idx;
+
+ if (cs == Qt::CaseSensitive) {
+ for (idx = 0; idx < sl; ++idx) {
+ hashNeedle = (hashNeedle << 1) + valueTypeToUtf16(*(n - idx));
+ hashHaystack = (hashHaystack << 1) + valueTypeToUtf16(*(h - idx));
+ }
+ hashHaystack -= valueTypeToUtf16(*haystack);
+
+ while (haystack >= end) {
+ hashHaystack += valueTypeToUtf16(*haystack);
+ if (hashHaystack == hashNeedle
+ && qt_compare_strings(needle0, sv(haystack), Qt::CaseSensitive) == 0)
+ return haystack - end;
+ --haystack;
+ REHASH(valueTypeToUtf16(haystack[sl]));
+ }
+ } else {
+ for (idx = 0; idx < sl; ++idx) {
+ hashNeedle = (hashNeedle << 1) + foldCaseHelper(n - idx, needle);
+ hashHaystack = (hashHaystack << 1) + foldCaseHelper(h - idx, end);
+ }
+ hashHaystack -= foldCaseHelper(haystack, end);
+
+ while (haystack >= end) {
+ hashHaystack += foldCaseHelper(haystack, end);
+ if (hashHaystack == hashNeedle
+ && qt_compare_strings(sv(haystack), needle0, Qt::CaseInsensitive) == 0)
+ return haystack - end;
+ --haystack;
+ REHASH(foldCaseHelper(haystack + sl, end));
+ }
+ }
+ return -1;
+}
+
qsizetype QtPrivate::findString(QStringView haystack, qsizetype from, QLatin1String needle, Qt::CaseSensitivity cs) noexcept
{
if (haystack.size() < needle.size())
@@ -11962,6 +12095,26 @@ qsizetype QtPrivate::findString(QLatin1String haystack, qsizetype from, QLatin1S
QStringView(reinterpret_cast<const QChar*>(n.constData()), n.size()), cs);
}
+qsizetype QtPrivate::lastIndexOf(QStringView haystack, qsizetype from, QStringView needle, Qt::CaseSensitivity cs) noexcept
+{
+ return qLastIndexOf(haystack, from, needle, cs);
+}
+
+qsizetype QtPrivate::lastIndexOf(QStringView haystack, qsizetype from, QLatin1String needle, Qt::CaseSensitivity cs) noexcept
+{
+ return qLastIndexOf(haystack, from, needle, cs);
+}
+
+qsizetype QtPrivate::lastIndexOf(QLatin1String haystack, qsizetype from, QStringView needle, Qt::CaseSensitivity cs) noexcept
+{
+ return qLastIndexOf(haystack, from, needle, cs);
+}
+
+qsizetype QtPrivate::lastIndexOf(QLatin1String haystack, qsizetype from, QLatin1String needle, Qt::CaseSensitivity cs) noexcept
+{
+ return qLastIndexOf(haystack, from, needle, cs);
+}
+
/*!
\since 4.8
diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h
index da8260a999..b56b37edf3 100644
--- a/src/corelib/tools/qstring.h
+++ b/src/corelib/tools/qstring.h
@@ -106,6 +106,9 @@ public:
Q_DECL_CONSTEXPR bool isNull() const noexcept { return !data(); }
Q_DECL_CONSTEXPR bool isEmpty() const noexcept { return !size(); }
+ template <typename...Args>
+ Q_REQUIRED_RESULT inline QString arg(Args &&...args) const;
+
Q_DECL_CONSTEXPR QLatin1Char at(int i) const
{ return Q_ASSERT(i >= 0), Q_ASSERT(i < size()), QLatin1Char(m_data[i]); }
Q_DECL_CONSTEXPR QLatin1Char operator[](int i) const { return at(i); }
@@ -145,6 +148,13 @@ public:
Q_REQUIRED_RESULT inline bool contains(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept
{ return indexOf(QStringView(&c, 1), 0, cs) != -1; }
+ Q_REQUIRED_RESULT int lastIndexOf(QStringView s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept
+ { return int(QtPrivate::lastIndexOf(*this, from, s, cs)); } // ### Qt6: qsize
+ Q_REQUIRED_RESULT int lastIndexOf(QLatin1String s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept
+ { return int(QtPrivate::lastIndexOf(*this, from, s, cs)); } // ### Qt6: qsize
+ Q_REQUIRED_RESULT inline int lastIndexOf(QChar c, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept
+ { return int(QtPrivate::lastIndexOf(*this, from, QStringView(&c, 1), cs)); } // ### Qt6: qsize
+
using value_type = const char;
using reference = value_type&;
using const_reference = reference;
@@ -214,7 +224,9 @@ private:
Q_DECLARE_TYPEINFO(QLatin1String, Q_MOVABLE_TYPE);
// Qt 4.x compatibility
-typedef QLatin1String QLatin1Literal;
+#if QT_DEPRECATED_SINCE(5, 14)
+QT_DEPRECATED_X("Use QLatin1String") typedef QLatin1String QLatin1Literal;
+#endif
//
// QLatin1String inline implementations
@@ -233,6 +245,8 @@ qsizetype QStringView::indexOf(QLatin1String s, qsizetype from, Qt::CaseSensitiv
{ return QtPrivate::findString(*this, from, s, cs); }
bool QStringView::contains(QLatin1String s, Qt::CaseSensitivity cs) const noexcept
{ return indexOf(s, 0, cs) != qsizetype(-1); }
+qsizetype QStringView::lastIndexOf(QLatin1String s, qsizetype from, Qt::CaseSensitivity cs) const noexcept
+{ return QtPrivate::lastIndexOf(*this, from, s, cs); }
class Q_CORE_EXPORT QString
{
@@ -355,9 +369,14 @@ public:
Q_REQUIRED_RESULT int indexOf(QStringView s, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept
{ return int(QtPrivate::findString(*this, from, s, cs)); } // ### Qt6: qsize
int lastIndexOf(QChar c, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
- int lastIndexOf(const QString &s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
int lastIndexOf(QLatin1String s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
+#if QT_STRINGVIEW_LEVEL < 2
+ int lastIndexOf(const QString &s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
int lastIndexOf(const QStringRef &s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
+#endif
+
+ Q_REQUIRED_RESULT int lastIndexOf(QStringView s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept
+ { return int(QtPrivate::lastIndexOf(*this, from, s, cs)); } // ### Qt6: qsize
inline bool contains(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
#if QT_STRINGVIEW_LEVEL < 2
@@ -905,8 +924,8 @@ private:
void reallocData(uint alloc, bool grow = false);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
void expand(int i);
-#endif
QString multiArg(int numArgs, const QString **args) const;
+#endif
static int compare_helper(const QChar *data1, int length1,
const QChar *data2, int length2,
Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept;
@@ -1035,30 +1054,30 @@ inline QString QString::arg(short a, int fieldWidth, int base, QChar fillChar) c
inline QString QString::arg(ushort a, int fieldWidth, int base, QChar fillChar) const
{ return arg(qulonglong(a), fieldWidth, base, fillChar); }
inline QString QString::arg(const QString &a1, const QString &a2) const
-{ const QString *args[2] = { &a1, &a2 }; return multiArg(2, args); }
+{ return qToStringViewIgnoringNull(*this).arg(a1, a2); }
inline QString QString::arg(const QString &a1, const QString &a2, const QString &a3) const
-{ const QString *args[3] = { &a1, &a2, &a3 }; return multiArg(3, args); }
+{ return qToStringViewIgnoringNull(*this).arg(a1, a2, a3); }
inline QString QString::arg(const QString &a1, const QString &a2, const QString &a3,
const QString &a4) const
-{ const QString *args[4] = { &a1, &a2, &a3, &a4 }; return multiArg(4, args); }
+{ return qToStringViewIgnoringNull(*this).arg(a1, a2, a3, a4); }
inline QString QString::arg(const QString &a1, const QString &a2, const QString &a3,
const QString &a4, const QString &a5) const
-{ const QString *args[5] = { &a1, &a2, &a3, &a4, &a5 }; return multiArg(5, args); }
+{ return qToStringViewIgnoringNull(*this).arg(a1, a2, a3, a4, a5); }
inline QString QString::arg(const QString &a1, const QString &a2, const QString &a3,
const QString &a4, const QString &a5, const QString &a6) const
-{ const QString *args[6] = { &a1, &a2, &a3, &a4, &a5, &a6 }; return multiArg(6, args); }
+{ return qToStringViewIgnoringNull(*this).arg(a1, a2, a3, a4, a5, a6); }
inline QString QString::arg(const QString &a1, const QString &a2, const QString &a3,
const QString &a4, const QString &a5, const QString &a6,
const QString &a7) const
-{ const QString *args[7] = { &a1, &a2, &a3, &a4, &a5, &a6, &a7 }; return multiArg(7, args); }
+{ return qToStringViewIgnoringNull(*this).arg(a1, a2, a3, a4, a5, a6, a7); }
inline QString QString::arg(const QString &a1, const QString &a2, const QString &a3,
const QString &a4, const QString &a5, const QString &a6,
const QString &a7, const QString &a8) const
-{ const QString *args[8] = { &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8 }; return multiArg(8, args); }
+{ return qToStringViewIgnoringNull(*this).arg(a1, a2, a3, a4, a5, a6, a7, a8); }
inline QString QString::arg(const QString &a1, const QString &a2, const QString &a3,
const QString &a4, const QString &a5, const QString &a6,
const QString &a7, const QString &a8, const QString &a9) const
-{ const QString *args[9] = { &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9 }; return multiArg(9, args); }
+{ return qToStringViewIgnoringNull(*this).arg(a1, a2, a3, a4, a5, a6, a7, a8, a9); }
inline QString QString::section(QChar asep, int astart, int aend, SectionFlags aflags) const
{ return section(QString(asep), astart, aend, aflags); }
@@ -1539,10 +1558,14 @@ public:
{ return int(QtPrivate::findString(*this, from, s, cs)); } // ### Qt6: qsize
int indexOf(QChar ch, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
int indexOf(QLatin1String str, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
+#if QT_STRINGVIEW_LEVEL < 2
+ int lastIndexOf(const QStringRef &str, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
int lastIndexOf(const QString &str, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
+#endif
int lastIndexOf(QChar ch, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
int lastIndexOf(QLatin1String str, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
- int lastIndexOf(const QStringRef &str, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
+ Q_REQUIRED_RESULT int lastIndexOf(QStringView s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept
+ { return int(QtPrivate::lastIndexOf(*this, from, s, cs)); } // ### Qt6: qsize
#if QT_STRINGVIEW_LEVEL < 2
inline bool contains(const QString &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
@@ -1959,6 +1982,58 @@ inline const QString &asString(const QString &s) { return s; }
inline QString &&asString(QString &&s) { return std::move(s); }
}
+//
+// QStringView::arg() implementation
+//
+
+namespace QtPrivate {
+
+struct ArgBase {
+ enum Tag : uchar { L1, U8, U16 } tag;
+};
+
+struct QStringViewArg : ArgBase {
+ QStringView string;
+ QStringViewArg() = default;
+ Q_DECL_CONSTEXPR explicit QStringViewArg(QStringView v) noexcept : ArgBase{U16}, string{v} {}
+};
+
+struct QLatin1StringArg : ArgBase {
+ QLatin1String string;
+ QLatin1StringArg() = default;
+ Q_DECL_CONSTEXPR explicit QLatin1StringArg(QLatin1String v) noexcept : ArgBase{L1}, string{v} {}
+};
+
+Q_REQUIRED_RESULT Q_CORE_EXPORT QString argToQString(QStringView pattern, size_t n, const ArgBase **args);
+Q_REQUIRED_RESULT Q_CORE_EXPORT QString argToQString(QLatin1String pattern, size_t n, const ArgBase **args);
+
+template <typename StringView, typename...Args>
+Q_REQUIRED_RESULT Q_ALWAYS_INLINE QString argToQStringDispatch(StringView pattern, const Args &...args)
+{
+ const ArgBase *argBases[] = {&args..., /* avoid zero-sized array */ nullptr};
+ return QtPrivate::argToQString(pattern, sizeof...(Args), argBases);
+}
+
+Q_DECL_CONSTEXPR inline QStringViewArg qStringLikeToArg(QStringView s) noexcept { return QStringViewArg{s}; }
+ inline QStringViewArg qStringLikeToArg(const QChar &c) noexcept { return QStringViewArg{QStringView{&c, 1}}; }
+Q_DECL_CONSTEXPR inline QLatin1StringArg qStringLikeToArg(QLatin1String s) noexcept { return QLatin1StringArg{s}; }
+
+} // namespace QtPrivate
+
+template <typename...Args>
+Q_ALWAYS_INLINE
+QString QStringView::arg(Args &&...args) const
+{
+ return QtPrivate::argToQStringDispatch(*this, QtPrivate::qStringLikeToArg(args)...);
+}
+
+template <typename...Args>
+Q_ALWAYS_INLINE
+QString QLatin1String::arg(Args &&...args) const
+{
+ return QtPrivate::argToQStringDispatch(*this, QtPrivate::qStringLikeToArg(args)...);
+}
+
QT_END_NAMESPACE
#if defined(QT_USE_FAST_OPERATOR_PLUS) || defined(QT_USE_QSTRINGBUILDER)
diff --git a/src/corelib/tools/qstringalgorithms.h b/src/corelib/tools/qstringalgorithms.h
index 51a86cfeb5..2b480b1e4c 100644
--- a/src/corelib/tools/qstringalgorithms.h
+++ b/src/corelib/tools/qstringalgorithms.h
@@ -80,6 +80,11 @@ Q_REQUIRED_RESULT Q_CORE_EXPORT Q_DECL_PURE_FUNCTION qsizetype findString(QStrin
Q_REQUIRED_RESULT Q_CORE_EXPORT Q_DECL_PURE_FUNCTION qsizetype findString(QLatin1String haystack, qsizetype from, QStringView needle, Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept;
Q_REQUIRED_RESULT Q_CORE_EXPORT Q_DECL_PURE_FUNCTION qsizetype findString(QLatin1String haystack, qsizetype from, QLatin1String needle, Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept;
+Q_REQUIRED_RESULT Q_CORE_EXPORT Q_DECL_PURE_FUNCTION qsizetype lastIndexOf(QStringView haystack, qsizetype from, QStringView needle, Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept;
+Q_REQUIRED_RESULT Q_CORE_EXPORT Q_DECL_PURE_FUNCTION qsizetype lastIndexOf(QStringView haystack, qsizetype from, QLatin1String needle, Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept;
+Q_REQUIRED_RESULT Q_CORE_EXPORT Q_DECL_PURE_FUNCTION qsizetype lastIndexOf(QLatin1String haystack, qsizetype from, QStringView needle, Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept;
+Q_REQUIRED_RESULT Q_CORE_EXPORT Q_DECL_PURE_FUNCTION qsizetype lastIndexOf(QLatin1String haystack, qsizetype from, QLatin1String needle, Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept;
+
Q_REQUIRED_RESULT Q_CORE_EXPORT Q_DECL_PURE_FUNCTION QStringView trimmed(QStringView s) noexcept;
Q_REQUIRED_RESULT Q_CORE_EXPORT Q_DECL_PURE_FUNCTION QLatin1String trimmed(QLatin1String s) noexcept;
diff --git a/src/corelib/tools/qstringmatcher.cpp b/src/corelib/tools/qstringmatcher.cpp
index 2e2ae18b9a..167a467480 100644
--- a/src/corelib/tools/qstringmatcher.cpp
+++ b/src/corelib/tools/qstringmatcher.cpp
@@ -149,7 +149,7 @@ static inline qsizetype bm_find(const ushort *uc, qsizetype l, qsizetype index,
Call setPattern() to give it a pattern to match.
*/
QStringMatcher::QStringMatcher()
- : d_ptr(0), q_cs(Qt::CaseSensitive)
+ : d_ptr(nullptr), q_cs(Qt::CaseSensitive)
{
memset(q_data, 0, sizeof(q_data));
}
@@ -161,7 +161,7 @@ QStringMatcher::QStringMatcher()
Call indexIn() to perform a search.
*/
QStringMatcher::QStringMatcher(const QString &pattern, Qt::CaseSensitivity cs)
- : d_ptr(0), q_pattern(pattern), q_cs(cs)
+ : d_ptr(nullptr), q_pattern(pattern), q_cs(cs)
{
p.uc = pattern.unicode();
p.len = pattern.size();
@@ -200,7 +200,7 @@ QStringMatcher::QStringMatcher(QStringView str, Qt::CaseSensitivity cs)
Copies the \a other string matcher to this string matcher.
*/
QStringMatcher::QStringMatcher(const QStringMatcher &other)
- : d_ptr(0)
+ : d_ptr(nullptr)
{
operator=(other);
}
diff --git a/src/corelib/tools/qstringview.cpp b/src/corelib/tools/qstringview.cpp
index 050097b443..cc852dd042 100644
--- a/src/corelib/tools/qstringview.cpp
+++ b/src/corelib/tools/qstringview.cpp
@@ -530,6 +530,24 @@ QT_BEGIN_NAMESPACE
*/
/*!
+ \fn QString QStringView::arg(Args &&...args) const
+ \fn QString QLatin1String::arg(Args &&...args) const
+ \since 5.14
+
+ Replaces occurrences of \c{%N} in this string with the corresponding
+ argument from \a args. The arguments are not positional: the first of
+ the \a args replaces the \c{%N} with the lowest \c{N} (all of them), the
+ second of the \a args the \c{%N} with the next-lowest \c{N} etc.
+
+ \c Args can consist of anything that implicitly converts to QStringView
+ or QLatin1String.
+
+ In addition, the following types are also supported: QChar, QLatin1Char.
+
+ \sa QString::arg()
+*/
+
+/*!
\fn QChar QStringView::front() const
Returns the first character in the string. Same as first().
@@ -755,6 +773,24 @@ QT_BEGIN_NAMESPACE
*/
/*!
+ \fn qsizetype QStringView::lastIndexOf(QStringView str, qsizetype from, Qt::CaseSensitivity cs) const
+ \fn qsizetype QStringView::lastIndexOf(QLatin1String l1, qsizetype from, Qt::CaseSensitivity cs) const
+ \fn qsizetype QStringView::lastIndexOf(QChar c, qsizetype from, Qt::CaseSensitivity cs) const
+ \since 5.14
+
+ Returns the index position of the last occurrence of the string-view \a str,
+ Latin-1 string \a l1, or character \a ch, respectively, in this string-view,
+ searching backward from index position \a from. If \a from is -1 (default),
+ the search starts at the last character; if \a from is -2, at the next to last
+ character and so on. Returns -1 if \a str is not found.
+
+ If \a cs is Qt::CaseSensitive (default), the search is case
+ sensitive; otherwise the search is case insensitive.
+
+ \sa QString::lastIndexOf()
+*/
+
+/*!
\fn QByteArray QStringView::toLatin1() const
Returns a Latin-1 representation of the string as a QByteArray.
diff --git a/src/corelib/tools/qstringview.h b/src/corelib/tools/qstringview.h
index 2c93b31385..b84b2995b9 100644
--- a/src/corelib/tools/qstringview.h
+++ b/src/corelib/tools/qstringview.h
@@ -226,6 +226,9 @@ public:
// QString API
//
+ template <typename...Args>
+ Q_REQUIRED_RESULT inline QString arg(Args &&...args) const; // defined in qstring.h
+
Q_REQUIRED_RESULT QByteArray toLatin1() const { return QtPrivate::convertToLatin1(*this); }
Q_REQUIRED_RESULT QByteArray toUtf8() const { return QtPrivate::convertToUtf8(*this); }
Q_REQUIRED_RESULT QByteArray toLocal8Bit() const { return QtPrivate::convertToLocal8Bit(*this); }
@@ -282,6 +285,12 @@ public:
{ return indexOf(s, 0, cs) != qsizetype(-1); }
Q_REQUIRED_RESULT inline bool contains(QLatin1String s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept;
+ Q_REQUIRED_RESULT qsizetype lastIndexOf(QChar c, qsizetype from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept
+ { return QtPrivate::lastIndexOf(*this, from, QStringView(&c, 1), cs); }
+ Q_REQUIRED_RESULT qsizetype lastIndexOf(QStringView s, qsizetype from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept
+ { return QtPrivate::lastIndexOf(*this, from, s, cs); }
+ Q_REQUIRED_RESULT inline qsizetype lastIndexOf(QLatin1String s, qsizetype from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept;
+
Q_REQUIRED_RESULT bool isRightToLeft() const noexcept
{ return QtPrivate::isRightToLeft(*this); }
diff --git a/src/corelib/tools/qvarlengtharray.h b/src/corelib/tools/qvarlengtharray.h
index 01fc63b677..a49e0af687 100644
--- a/src/corelib/tools/qvarlengtharray.h
+++ b/src/corelib/tools/qvarlengtharray.h
@@ -44,6 +44,7 @@
#include <QtCore/qglobal.h>
#include <QtCore/qalgorithms.h>
#include <QtCore/qcontainertools_impl.h>
+#include <QtCore/qhashfunctions.h>
#include <algorithm>
#include <initializer_list>
@@ -599,6 +600,13 @@ inline bool operator>=(const QVarLengthArray<T, Prealloc1> &lhs, const QVarLengt
return !(lhs < rhs);
}
+template <typename T, int Prealloc>
+uint qHash(const QVarLengthArray<T, Prealloc> &key, uint seed = 0)
+ noexcept(noexcept(qHashRange(key.cbegin(), key.cend(), seed)))
+{
+ return qHashRange(key.cbegin(), key.cend(), seed);
+}
+
QT_END_NAMESPACE
#endif // QVARLENGTHARRAY_H
diff --git a/src/corelib/tools/qvarlengtharray.qdoc b/src/corelib/tools/qvarlengtharray.qdoc
index 80769e3769..e5ba47b8ef 100644
--- a/src/corelib/tools/qvarlengtharray.qdoc
+++ b/src/corelib/tools/qvarlengtharray.qdoc
@@ -903,3 +903,11 @@
\sa indexOf(), lastIndexOf()
*/
+/*!
+ template <typename T, int Prealloc> uint qHash(const QVarLengthArray<T, Prealloc> &key, uint seed = 0)
+ \relates QVarLengthArray
+ \since 5.14
+
+ Returns the hash value for \a key, using \a seed to seed the
+ calculation.
+*/
diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h
index 5d68a283bd..65a5174abf 100644
--- a/src/corelib/tools/qvector.h
+++ b/src/corelib/tools/qvector.h
@@ -80,6 +80,7 @@ public:
QVector<T> &operator=(std::initializer_list<T> args);
template <typename InputIterator, QtPrivate::IfIsInputIterator<InputIterator> = true>
inline QVector(InputIterator first, InputIterator last);
+ explicit QVector(QArrayDataPointerRef<T> ref) noexcept : d(ref.ptr) {}
bool operator==(const QVector<T> &v) const;
inline bool operator!=(const QVector<T> &v) const { return !(*this == v); }
diff --git a/src/corelib/tools/qvector.qdoc b/src/corelib/tools/qvector.qdoc
index c1b5054f93..4c442511ea 100644
--- a/src/corelib/tools/qvector.qdoc
+++ b/src/corelib/tools/qvector.qdoc
@@ -251,6 +251,11 @@
The value type of \c InputIterator must be convertible to \c T.
*/
+/*!
+ \fn template <typename T> QVector<T>::QVector(QArrayDataPointerRef<T> ref)
+ \internal
+*/
+
/*! \fn template <typename T> QVector<T>::~QVector()
Destroys the vector.
diff --git a/src/dbus/doc/snippets/cmake/examples.cmake b/src/dbus/doc/snippets/cmake/examples.cmake
new file mode 100644
index 0000000000..cb4f86844f
--- /dev/null
+++ b/src/dbus/doc/snippets/cmake/examples.cmake
@@ -0,0 +1,3 @@
+#! [qt5_add_dbus_adaptor]
+qt5_add_dbus_adaptor(GENERATED_SOURCES org.example.chat.xml chat.h ChatMainWindow)
+#! [qt5_add_dbus_adaptor]
diff --git a/src/dbus/doc/src/dontdocument.qdoc b/src/dbus/doc/src/dontdocument.qdoc
new file mode 100644
index 0000000000..bbb8acb53c
--- /dev/null
+++ b/src/dbus/doc/src/dontdocument.qdoc
@@ -0,0 +1,30 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \dontdocument (QTypeInfo QMetaTypeId QDBusAbstractInterfaceBase QDBusPendingReplyData QMetaTypeId2)
+*/
diff --git a/src/dbus/doc/src/qtdbus-cmake.qdoc b/src/dbus/doc/src/qtdbus-cmake.qdoc
new file mode 100644
index 0000000000..de127fa9f4
--- /dev/null
+++ b/src/dbus/doc/src/qtdbus-cmake.qdoc
@@ -0,0 +1,224 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+\page qtdbus-cmake-qt5-add-dbus-interface.html
+\ingroup cmake-commands-qtdbus
+
+\title qt5_add_dbus_interface
+
+\brief Generates C++ sources implementing an interface for a D-Bus interface
+description file.
+
+\section1 Synopsis
+
+\badcode
+qt5_add_dbus_interface(<VAR> dbus_spec basename)
+\endcode
+
+\section1 Description
+
+Generates C++ sources implementing an interface for a D-Bus interface description
+file defined in \c{dbus_spec}. The generated files are named after \c{basename}:
+\c{basename.h}, \c{basename.cpp}, \c{basename.moc}. The paths of the files
+are added to \c{<VAR>}.
+
+The function sets up a call to the \l{Qt D-Bus XML compiler (qdbusxml2cpp)}
+in interface (proxy) mode. By default, \c{qdbusxml2cpp} generates a C++
+class named after the interface name, with a namespaced alias:
+
+\table
+\header
+ \li D-Bus Interface Name
+ \li Class name
+ \li Namespaced name
+\row
+ \li \c{org.example.chat}
+ \li \c{OrgExampleChatInterface}
+ \li \c{org.example.chat}
+\endtable
+
+\section1 Options
+
+Options can be set using \c set_source_file_property on the \c dbus_spec:
+
+\table
+\header
+ \li Option
+ \li Value
+ \li Description
+\row
+ \li \c CLASSNAME
+ \li \c class_name
+ \li Override the default interface class name with \c{class_name}.
+\row
+ \li \c NO_NAMESPACE
+ \li boolean
+ \li Do not generate the namespaced name if set to \c{ON}.
+\row
+ \li \c INCLUDE
+ \li \c path
+ \li Add an \c{#include "path"} in the generated code.
+\endtable
+*/
+
+/*!
+\page qtdbus-cmake-qt5-add-dbus-interfaces.html
+\ingroup cmake-commands-qtdbus
+
+\title qt5_add_dbus_interfaces
+
+\brief Generates C++ sources implementing interfaces for D-Bus interface
+description files.
+
+\section1 Synopsis
+
+\badcode
+qt5_add_dbus_interfaces(<VAR> dbus_spec1 [dbus_spec2 ...])
+\endcode
+
+\section1 Description
+
+Generates C++ sources implementing D-Bus interfaces defined in \c{dbus_spec1},
+\c{dbus_spec2}, where each argument needs to be the path to a valid D-Bus
+interface description file. The paths of the generated files are added to
+\c{<VAR>}.
+
+For each argument, a call to the \l{Qt D-Bus XML compiler (qdbusxml2cpp)}
+in interface (proxy) mode is set up.
+
+The generated C++ source files are named after the XML file: For the file
+\c{org.example.chat.xml} the generated header will be named
+\c{orgexamplechatinterface.h}.
+
+\section1 Options
+
+Options can be set using \c set_source_file_property on each of the file
+arguments:
+
+\table
+\header
+ \li Option
+ \li Value
+ \li Description
+\row
+ \li \c CLASSNAME
+ \li \c class_name
+ \li Override the default interface class name with \c{class_name}.
+\row
+ \li \c NO_NAMESPACE
+ \li boolean
+ \li Do not generate the namespaced name if set to \c{ON}.
+\row
+ \li \c INCLUDE
+ \li \c path
+ \li Add an \c{#include "path"} in the generated code.
+\endtable
+*/
+
+/*!
+\page qtdbus-cmake-qt5-generate-dbus-interface.html
+\ingroup cmake-commands-qtdbus
+
+\title qt5_generate_dbus_interface
+
+\brief Generates a D-Bus interface from a header file.
+
+\section1 Synopsis
+
+\badcode
+qt5_generate_dbus_interface(header
+ [customName]
+ [OPTIONS options]
+)
+\endcode
+
+\section1 Description
+
+Parses the C++ source or header file containing a QObject-derived class
+declaration and generates a file containing the D-BUS Introspection XML.
+
+By default, the generated XML file is stored in the current binary directory,
+and has the same base name as the header. You can specify a different name or
+path by adding \c{customName} as an optional second argument.
+
+\section1 Options
+
+The function sets up a call to the \c{qdbuscpp2xml} command line tool. Further
+arguments to the tool can be set after \c{OPTIONS}.
+*/
+
+/*!
+\page qtdbus-cmake-qt5-add-dbus-adaptor.html
+\ingroup cmake-commands-qtdbus
+
+\title qt5_add_dbus_adaptor
+
+\brief Generates an adaptor class for a D-Bus interface.
+
+\section1 Synopsis
+
+\badcode
+qt5_add_dbus_adaptor(<VAR> dbus_spec header parent_class
+ [basename]
+ [classname])
+\endcode
+
+\section1 Description
+
+Generates a C++ header file implementing an adaptor for a D-Bus interface
+description file defined in \c{dbus_spec}. The path of the generated file is
+added to \c{<VAR>}. The generated adaptor class takes a pointer to
+\c{parent_class} as QObject parent. \c{parent_class} should be declared in
+\c{header}, which is included in the generated code as \c{#include "header"}.
+
+The function sets up a call to the \l{Qt D-Bus XML compiler (qdbusxml2cpp)}
+in adaptor mode. The default file and class name are generated from the last
+segment in the \c{dbus_spec} base name:
+
+\table
+\header
+ \li XML file
+ \li Header file
+ \li Class name
+\row
+ \li \c{org.example.chat}
+ \li \c{chatadaptor.h}
+ \li \c{ChatAdaptor}
+\endtable
+
+
+You can change the name of the header file to be generated by passing
+\c{basename} as the fifth argument. The \c{.h} suffix is always added.
+
+You can change the default class name by passing \c{classname} as the sixth
+argument.
+
+\section1 Examples
+
+\snippet cmake/examples.cmake qt5_add_dbus_adaptor
+*/
diff --git a/src/dbus/qdbusabstractinterface.cpp b/src/dbus/qdbusabstractinterface.cpp
index 220223d74d..87de784fc0 100644
--- a/src/dbus/qdbusabstractinterface.cpp
+++ b/src/dbus/qdbusabstractinterface.cpp
@@ -164,7 +164,7 @@ bool QDBusAbstractInterfacePrivate::property(const QMetaProperty &mp, void *retu
"used to read property %s.%s",
mp.typeName(), qPrintable(interface), mp.name());
lastError = QDBusError(QDBusError::Failed,
- QString::fromLatin1("Unregistered type %1 cannot be handled")
+ QLatin1String("Unregistered type %1 cannot be handled")
.arg(QLatin1String(mp.typeName())));
return false;
}
@@ -220,15 +220,15 @@ bool QDBusAbstractInterfacePrivate::property(const QMetaProperty &mp, void *retu
}
// there was an error...
- QString errmsg = QLatin1String("Unexpected `%1' (%2) when retrieving property `%3.%4' "
- "(expected type `%5' (%6))");
+ const auto errmsg = QLatin1String("Unexpected `%1' (%2) when retrieving property `%3.%4' "
+ "(expected type `%5' (%6))");
lastError = QDBusError(QDBusError::InvalidSignature,
- errmsg.arg(QString::fromLatin1(foundType),
- QString::fromLatin1(foundSignature),
+ errmsg.arg(QLatin1String(foundType),
+ QLatin1String(foundSignature),
interface,
- QString::fromUtf8(mp.name()),
- QString::fromLatin1(mp.typeName()),
- QString::fromLatin1(expectedSignature)));
+ QLatin1String(mp.name()),
+ QLatin1String(mp.typeName()),
+ QLatin1String(expectedSignature)));
return false;
}
@@ -691,18 +691,20 @@ void QDBusAbstractInterface::internalPropSet(const char *propname, const QVarian
}
/*!
- Calls the method \a method on this interface and passes the parameters to this function to the
- method.
+ \fn QDBusAbstractInterface::call(const QString &message)
+ \internal
+*/
+
+/*!
+ \fn QDBusAbstractInterface::call(const QString &message, Args&&...args)
+
+ Calls the method \a method on this interface and passes \a args to the method.
+ All \a args must be convertible to QVariant.
The parameters to \c call are passed on to the remote function via D-Bus as input
arguments. Output arguments are returned in the QDBusMessage reply. If the reply is an error
reply, lastError() will also be set to the contents of the error message.
- This function can be used with up to 8 parameters, passed in arguments \a arg1, \a arg2,
- \a arg3, \a arg4, \a arg5, \a arg6, \a arg7 and \a arg8. If you need more than 8
- parameters or if you have a variable number of parameters to be passed, use
- callWithArgumentList().
-
It can be used the following way:
\snippet code/src_qdbus_qdbusabstractinterface.cpp 0
@@ -710,6 +712,19 @@ void QDBusAbstractInterface::internalPropSet(const char *propname, const QVarian
This example illustrates function calling with 0, 1 and 2 parameters and illustrates different
parameter types passed in each (the first call to \c "ProcessWorkUnicode" will contain one
Unicode string, the second call to \c "ProcessWork" will contain one string and one byte array).
+
+ \note Before Qt 5.14, this function accepted a maximum of just eight (8) arguments.
+
+ \sa callWithArgumentList()
+*/
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+/*!
+ \internal
+
+ This function exists for binary compatibility with Qt versions < 5.14.
+ Programs recompiled against Qt 5.14 will use the variadic template function
+ instead.
*/
QDBusMessage QDBusAbstractInterface::call(const QString &method, const QVariant &arg1,
const QVariant &arg2,
@@ -722,27 +737,44 @@ QDBusMessage QDBusAbstractInterface::call(const QString &method, const QVariant
{
return call(QDBus::AutoDetect, method, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
}
+#endif
+
+/*!
+ \fn QDBusAbstractInterface::call(QDBus::CallMode mode, const QString &message)
+ \internal
+*/
/*!
+ \fn QDBusAbstractInterface::call(QDBus::CallMode mode, const QString &message, Args&&...args)
+
\overload
- Calls the method \a method on this interface and passes the
- parameters to this function to the method. If \a mode is \c
- NoWaitForReply, then this function will return immediately after
+ Calls the method \a method on this interface and passes \a args to the method.
+ All \a args must be convertible to QVariant.
+
+ If \a mode is \c NoWaitForReply, then this function will return immediately after
placing the call, without waiting for a reply from the remote
method. Otherwise, \a mode indicates whether this function should
activate the Qt Event Loop while waiting for the reply to arrive.
- This function can be used with up to 8 parameters, passed in arguments \a arg1, \a arg2,
- \a arg3, \a arg4, \a arg5, \a arg6, \a arg7 and \a arg8. If you need more than 8
- parameters or if you have a variable number of parameters to be passed, use
- callWithArgumentList().
-
If this function reenters the Qt event loop in order to wait for the
reply, it will exclude user input. During the wait, it may deliver
signals and other method calls to your application. Therefore, it
must be prepared to handle a reentrancy whenever a call is placed
with call().
+
+ \note Before Qt 5.14, this function accepted a maximum of just eight (8) arguments.
+
+ \sa callWithArgumentList()
+*/
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+/*!
+ \internal
+
+ This function exists for binary compatibility with Qt versions < 5.14.
+ Programs recompiled against Qt 5.14 will use the variadic template function
+ instead.
*/
QDBusMessage QDBusAbstractInterface::call(QDBus::CallMode mode, const QString &method,
const QVariant &arg1,
@@ -787,22 +819,23 @@ QDBusMessage QDBusAbstractInterface::call(QDBus::CallMode mode, const QString &m
return callWithArgumentList(mode, method, argList);
}
+#endif // Qt 5
+/*!
+ \fn QDBusAbstractInterface::asyncCall(const QString &message)
+ \internal
+*/
/*!
- \since 4.5
- Calls the method \a method on this interface and passes the parameters to this function to the
- method.
+ \fn QDBusAbstractInterface::asyncCall(const QString &message, Args&&...args)
+
+ Calls the method \a method on this interface and passes \a args to the method.
+ All \a args must be convertible to QVariant.
The parameters to \c call are passed on to the remote function via D-Bus as input
arguments. The returned QDBusPendingCall object can be used to find out information about
the reply.
- This function can be used with up to 8 parameters, passed in arguments \a arg1, \a arg2,
- \a arg3, \a arg4, \a arg5, \a arg6, \a arg7 and \a arg8. If you need more than 8
- parameters or if you have a variable number of parameters to be passed, use
- asyncCallWithArgumentList().
-
It can be used the following way:
\snippet code/src_qdbus_qdbusabstractinterface.cpp 1
@@ -810,6 +843,19 @@ QDBusMessage QDBusAbstractInterface::call(QDBus::CallMode mode, const QString &m
This example illustrates function calling with 0, 1 and 2 parameters and illustrates different
parameter types passed in each (the first call to \c "ProcessWorkUnicode" will contain one
Unicode string, the second call to \c "ProcessWork" will contain one string and one byte array).
+
+ \note Before Qt 5.14, this function accepted a maximum of just eight (8) arguments.
+
+ \sa asyncCallWithArgumentList()
+*/
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+/*!
+ \internal
+
+ This function exists for binary compatibility with Qt versions < 5.14.
+ Programs recompiled against Qt 5.14 will use the variadic template function
+ instead.
*/
QDBusPendingCall QDBusAbstractInterface::asyncCall(const QString &method, const QVariant &arg1,
const QVariant &arg2,
@@ -853,6 +899,7 @@ QDBusPendingCall QDBusAbstractInterface::asyncCall(const QString &method, const
return asyncCallWithArgumentList(method, argList);
}
+#endif // Qt 5
/*!
\internal
@@ -865,6 +912,24 @@ QDBusMessage QDBusAbstractInterface::internalConstCall(QDBus::CallMode mode,
return const_cast<QDBusAbstractInterface*>(this)->callWithArgumentList(mode, method, args);
}
+QDBusMessage QDBusAbstractInterface::doCall(QDBus::CallMode mode, const QString &method, const QVariant *args, size_t numArgs)
+{
+ QList<QVariant> list;
+ list.reserve(int(numArgs));
+ for (size_t i = 0; i < numArgs; ++i)
+ list.append(args[i]);
+ return callWithArgumentList(mode, method, list);
+}
+
+QDBusPendingCall QDBusAbstractInterface::doAsyncCall(const QString &method, const QVariant *args, size_t numArgs)
+{
+ QList<QVariant> list;
+ list.reserve(int(numArgs));
+ for (size_t i = 0; i < numArgs; ++i)
+ list.append(args[i]);
+ return asyncCallWithArgumentList(method, list);
+}
+
QT_END_NAMESPACE
#endif // QT_NO_DBUS
diff --git a/src/dbus/qdbusabstractinterface.h b/src/dbus/qdbusabstractinterface.h
index d6b0870787..4f4c7430a5 100644
--- a/src/dbus/qdbusabstractinterface.h
+++ b/src/dbus/qdbusabstractinterface.h
@@ -49,6 +49,7 @@
#include <QtDBus/qdbusmessage.h>
#include <QtDBus/qdbusextratypes.h>
#include <QtDBus/qdbusconnection.h>
+#include <QtDBus/qdbuspendingcall.h>
#ifdef interface
#undef interface
@@ -98,26 +99,52 @@ public:
void setTimeout(int timeout);
int timeout() const;
+ QDBusMessage call(const QString &method)
+ {
+ return doCall(QDBus::AutoDetect, method, nullptr, 0);
+ }
+
+ template <typename...Args>
+ QDBusMessage call(const QString &method, Args &&...args)
+ {
+ const QVariant variants[] = { QVariant(std::forward<Args>(args))... };
+ return doCall(QDBus::AutoDetect, method, variants, sizeof...(args));
+ }
+
+ QDBusMessage call(QDBus::CallMode mode, const QString &method)
+ {
+ return doCall(mode, method, nullptr, 0);
+ }
+
+ template <typename...Args>
+ QDBusMessage call(QDBus::CallMode mode, const QString &method, Args &&...args)
+ {
+ const QVariant variants[] = { QVariant(std::forward<Args>(args))... };
+ return doCall(mode, method, variants, sizeof...(args));
+ }
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QDBusMessage call(const QString &method,
- const QVariant &arg1 = QVariant(),
- const QVariant &arg2 = QVariant(),
- const QVariant &arg3 = QVariant(),
- const QVariant &arg4 = QVariant(),
- const QVariant &arg5 = QVariant(),
- const QVariant &arg6 = QVariant(),
- const QVariant &arg7 = QVariant(),
- const QVariant &arg8 = QVariant());
+ const QVariant &arg1,
+ const QVariant &arg2,
+ const QVariant &arg3,
+ const QVariant &arg4,
+ const QVariant &arg5,
+ const QVariant &arg6,
+ const QVariant &arg7,
+ const QVariant &arg8);
QDBusMessage call(QDBus::CallMode mode,
const QString &method,
- const QVariant &arg1 = QVariant(),
- const QVariant &arg2 = QVariant(),
- const QVariant &arg3 = QVariant(),
- const QVariant &arg4 = QVariant(),
- const QVariant &arg5 = QVariant(),
- const QVariant &arg6 = QVariant(),
- const QVariant &arg7 = QVariant(),
- const QVariant &arg8 = QVariant());
+ const QVariant &arg1,
+ const QVariant &arg2,
+ const QVariant &arg3,
+ const QVariant &arg4,
+ const QVariant &arg5,
+ const QVariant &arg6,
+ const QVariant &arg7,
+ const QVariant &arg8);
+#endif // Qt 5
QDBusMessage callWithArgumentList(QDBus::CallMode mode,
const QString &method,
@@ -130,15 +157,30 @@ public:
const QList<QVariant> &args,
QObject *receiver, const char *member);
+ QDBusPendingCall asyncCall(const QString &method)
+ {
+ return doAsyncCall(method, nullptr, 0);
+ }
+
+ template <typename...Args>
+ QDBusPendingCall asyncCall(const QString &method, Args&&...args)
+ {
+ const QVariant variants[] = { QVariant(std::forward<Args>(args))... };
+ return doAsyncCall(method, variants, sizeof...(args));
+ }
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QDBusPendingCall asyncCall(const QString &method,
- const QVariant &arg1 = QVariant(),
- const QVariant &arg2 = QVariant(),
- const QVariant &arg3 = QVariant(),
- const QVariant &arg4 = QVariant(),
- const QVariant &arg5 = QVariant(),
- const QVariant &arg6 = QVariant(),
- const QVariant &arg7 = QVariant(),
- const QVariant &arg8 = QVariant());
+ const QVariant &arg1,
+ const QVariant &arg2,
+ const QVariant &arg3,
+ const QVariant &arg4,
+ const QVariant &arg5,
+ const QVariant &arg6,
+ const QVariant &arg7,
+ const QVariant &arg8);
+#endif // Qt 5
+
QDBusPendingCall asyncCallWithArgumentList(const QString &method,
const QList<QVariant> &args);
@@ -156,6 +198,10 @@ protected:
const QList<QVariant> &args = QList<QVariant>()) const;
private:
+ QDBusMessage doCall(QDBus::CallMode mode, const QString &method, const QVariant *args, size_t numArgs);
+ QDBusPendingCall doAsyncCall(const QString &method, const QVariant *args, size_t numArgs);
+
+private:
Q_DECLARE_PRIVATE(QDBusAbstractInterface)
Q_PRIVATE_SLOT(d_func(), void _q_serviceOwnerChanged(QString,QString,QString))
};
diff --git a/src/dbus/qdbusargument.cpp b/src/dbus/qdbusargument.cpp
index 2d1373006d..764bc24165 100644
--- a/src/dbus/qdbusargument.cpp
+++ b/src/dbus/qdbusargument.cpp
@@ -111,7 +111,7 @@ bool QDBusArgumentPrivate::checkWrite(QDBusArgumentPrivate *&d)
if (!d->marshaller()->ok)
return false;
- if (d->message && d->ref.load() != 1) {
+ if (d->message && d->ref.loadRelaxed() != 1) {
QDBusMarshaller *dd = new QDBusMarshaller(d->capabilities);
dd->message = q_dbus_message_copy(d->message);
q_dbus_message_iter_init_append(dd->message, &dd->iterator);
@@ -152,7 +152,7 @@ bool QDBusArgumentPrivate::checkReadAndDetach(QDBusArgumentPrivate *&d)
if (!checkRead(d))
return false; // don't bother
- if (d->ref.load() == 1)
+ if (d->ref.loadRelaxed() == 1)
return true; // no need to detach
QDBusDemarshaller *dd = new QDBusDemarshaller(d->capabilities);
diff --git a/src/dbus/qdbusintegrator.cpp b/src/dbus/qdbusintegrator.cpp
index 640acc2425..9306498f89 100644
--- a/src/dbus/qdbusintegrator.cpp
+++ b/src/dbus/qdbusintegrator.cpp
@@ -340,8 +340,9 @@ static QByteArray buildMatchRule(const QString &service,
const QString &objectPath, const QString &interface,
const QString &member, const QDBusConnectionPrivate::ArgMatchRules &argMatch, const QString & /*signature*/)
{
- QString result = QLatin1String("type='signal',");
- QString keyValue = QLatin1String("%1='%2',");
+ QString result;
+ result += QLatin1String("type='signal',");
+ const auto keyValue = QLatin1String("%1='%2',");
if (!service.isEmpty())
result += keyValue.arg(QLatin1String("sender"), service);
@@ -354,13 +355,13 @@ static QByteArray buildMatchRule(const QString &service,
// add the argument string-matching now
if (!argMatch.args.isEmpty()) {
- keyValue = QLatin1String("arg%1='%2',");
+ const QString keyValue = QLatin1String("arg%1='%2',");
for (int i = 0; i < argMatch.args.count(); ++i)
if (!argMatch.args.at(i).isNull())
result += keyValue.arg(i).arg(argMatch.args.at(i));
}
if (!argMatch.arg0namespace.isEmpty()) {
- result += QStringLiteral("arg0namespace='%1',").arg(argMatch.arg0namespace);
+ result += QLatin1String("arg0namespace='%1',").arg(argMatch.arg0namespace);
}
result.chop(1); // remove ending comma
@@ -534,7 +535,7 @@ qDBusSignalFilter(DBusConnection *connection, DBusMessage *message, void *data)
bool QDBusConnectionPrivate::handleMessage(const QDBusMessage &amsg)
{
- if (!ref.load())
+ if (!ref.loadRelaxed())
return false;
// local message are always delivered, regardless of filtering
@@ -1077,7 +1078,7 @@ QDBusConnectionPrivate::~QDBusConnectionPrivate()
if (lastMode == ClientMode || lastMode == PeerMode) {
// the bus service object holds a reference back to us;
// we need to destroy it before we finish destroying ourselves
- Q_ASSERT(ref.load() == 0);
+ Q_ASSERT(ref.loadRelaxed() == 0);
QObject *obj = (QObject *)busService;
if (obj) {
disconnect(obj, nullptr, this, nullptr);
@@ -1369,19 +1370,19 @@ void QDBusConnectionPrivate::sendError(const QDBusMessage &msg, QDBusError::Erro
if (msg.interface().isEmpty())
interfaceMsg = QLatin1String("any interface");
else
- interfaceMsg = QString::fromLatin1("interface '%1'").arg(msg.interface());
+ interfaceMsg = QLatin1String("interface '%1'").arg(msg.interface());
send(msg.createErrorReply(code,
- QString::fromLatin1("No such method '%1' in %2 at object path '%3' "
- "(signature '%4')")
+ QLatin1String("No such method '%1' in %2 at object path '%3' "
+ "(signature '%4')")
.arg(msg.member(), interfaceMsg, msg.path(), msg.signature())));
} else if (code == QDBusError::UnknownInterface) {
send(msg.createErrorReply(QDBusError::UnknownInterface,
- QString::fromLatin1("No such interface '%1' at object path '%2'")
+ QLatin1String("No such interface '%1' at object path '%2'")
.arg(msg.interface(), msg.path())));
} else if (code == QDBusError::UnknownObject) {
send(msg.createErrorReply(QDBusError::UnknownObject,
- QString::fromLatin1("No such object path '%1'").arg(msg.path())));
+ QLatin1String("No such object path '%1'").arg(msg.path())));
}
}
@@ -1551,8 +1552,8 @@ void QDBusConnectionPrivate::handleObjectCall(const QDBusMessage &msg)
objThread = result.obj->thread();
if (!objThread) {
send(msg.createErrorReply(QDBusError::InternalError,
- QString::fromLatin1("Object '%1' (at path '%2')"
- " has no thread. Cannot deliver message.")
+ QLatin1String("Object '%1' (at path '%2')"
+ " has no thread. Cannot deliver message.")
.arg(result.obj->objectName(), msg.path())));
return;
}
@@ -2082,7 +2083,7 @@ QDBusMessage QDBusConnectionPrivate::sendWithReplyLocal(const QDBusMessage &mess
if (interface.isEmpty())
interface = QLatin1String("<no-interface>");
return QDBusMessage::createError(QDBusError::InternalError,
- QString::fromLatin1("Internal error trying to call %1.%2 at %3 (signature '%4'")
+ QLatin1String("Internal error trying to call %1.%2 at %3 (signature '%4'")
.arg(interface, message.member(),
message.path(), message.signature()));
}
@@ -2126,11 +2127,11 @@ QDBusPendingCallPrivate *QDBusConnectionPrivate::sendWithReplyAsync(const QDBusM
if ((receiver && returnMethod) || errorMethod) {
// no one waiting, will delete pcall in processFinishedCall()
- pcall->ref.store(1);
+ pcall->ref.storeRelaxed(1);
} else {
// set double ref to prevent race between processFinishedCall() and ref counting
// by QDBusPendingCall::QExplicitlySharedDataPointer<QDBusPendingCallPrivate>
- pcall->ref.store(2);
+ pcall->ref.storeRelaxed(2);
}
if (isLoopback) {
diff --git a/src/dbus/qdbusinternalfilters.cpp b/src/dbus/qdbusinternalfilters.cpp
index 0ef5061b5f..edee4fc1e5 100644
--- a/src/dbus/qdbusinternalfilters.cpp
+++ b/src/dbus/qdbusinternalfilters.cpp
@@ -203,7 +203,7 @@ QString qDBusIntrospectObject(const QDBusConnectionPrivate::ObjectTreeNode &node
static inline QDBusMessage interfaceNotFoundError(const QDBusMessage &msg, const QString &interface_name)
{
return msg.createErrorReply(QDBusError::UnknownInterface,
- QString::fromLatin1("Interface %1 was not found in object %2")
+ QLatin1String("Interface %1 was not found in object %2")
.arg(interface_name, msg.path()));
}
@@ -211,10 +211,10 @@ static inline QDBusMessage
propertyNotFoundError(const QDBusMessage &msg, const QString &interface_name, const QByteArray &property_name)
{
return msg.createErrorReply(QDBusError::UnknownProperty,
- QString::fromLatin1("Property %1%2%3 was not found in object %4")
+ QLatin1String("Property %1%2%3 was not found in object %4")
.arg(interface_name,
- QString::fromLatin1(interface_name.isEmpty() ? "" : "."),
- QString::fromLatin1(property_name),
+ QLatin1String(interface_name.isEmpty() ? "" : "."),
+ QLatin1String(property_name),
msg.path()));
}
@@ -302,16 +302,16 @@ static QDBusMessage propertyWriteReply(const QDBusMessage &msg, const QString &i
return propertyNotFoundError(msg, interface_name, property_name);
case PropertyTypeMismatch:
return msg.createErrorReply(QDBusError::InvalidArgs,
- QString::fromLatin1("Invalid arguments for writing to property %1%2%3")
+ QLatin1String("Invalid arguments for writing to property %1%2%3")
.arg(interface_name,
- QString::fromLatin1(interface_name.isEmpty() ? "" : "."),
- QString::fromLatin1(property_name)));
+ QLatin1String(interface_name.isEmpty() ? "" : "."),
+ QLatin1String(property_name)));
case PropertyReadOnly:
return msg.createErrorReply(QDBusError::PropertyReadOnly,
- QString::fromLatin1("Property %1%2%3 is read-only")
+ QLatin1String("Property %1%2%3 is read-only")
.arg(interface_name,
- QString::fromLatin1(interface_name.isEmpty() ? "" : "."),
- QString::fromLatin1(property_name)));
+ QLatin1String(interface_name.isEmpty() ? "" : "."),
+ QLatin1String(property_name)));
case PropertyWriteFailed:
return msg.createErrorReply(QDBusError::InternalError,
QString::fromLatin1("Internal error"));
diff --git a/src/dbus/qdbusmarshaller.cpp b/src/dbus/qdbusmarshaller.cpp
index 4ea6cefff6..8e0b3e4598 100644
--- a/src/dbus/qdbusmarshaller.cpp
+++ b/src/dbus/qdbusmarshaller.cpp
@@ -211,7 +211,7 @@ inline bool QDBusMarshaller::append(const QDBusVariant &arg)
qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. "
"Use qDBusRegisterMetaType to register it",
QMetaType::typeName(id), id);
- error(QString::fromLatin1("Unregistered type %1 passed in arguments")
+ error(QLatin1String("Unregistered type %1 passed in arguments")
.arg(QLatin1String(QMetaType::typeName(id))));
return false;
}
@@ -253,7 +253,7 @@ inline QDBusMarshaller *QDBusMarshaller::beginArray(int id)
qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. "
"Use qDBusRegisterMetaType to register it",
QMetaType::typeName(id), id);
- error(QString::fromLatin1("Unregistered type %1 passed in arguments")
+ error(QLatin1String("Unregistered type %1 passed in arguments")
.arg(QLatin1String(QMetaType::typeName(id))));
return this;
}
@@ -268,14 +268,14 @@ inline QDBusMarshaller *QDBusMarshaller::beginMap(int kid, int vid)
qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. "
"Use qDBusRegisterMetaType to register it",
QMetaType::typeName(kid), kid);
- error(QString::fromLatin1("Unregistered type %1 passed in arguments")
+ error(QLatin1String("Unregistered type %1 passed in arguments")
.arg(QLatin1String(QMetaType::typeName(kid))));
return this;
}
if (ksignature[1] != 0 || !QDBusUtil::isValidBasicType(*ksignature)) {
qWarning("QDBusMarshaller: type '%s' (%d) cannot be used as the key type in a D-BUS map.",
QMetaType::typeName(kid), kid);
- error(QString::fromLatin1("Type %1 passed in arguments cannot be used as a key in a map")
+ error(QLatin1String("Type %1 passed in arguments cannot be used as a key in a map")
.arg(QLatin1String(QMetaType::typeName(kid))));
return this;
}
@@ -286,7 +286,7 @@ inline QDBusMarshaller *QDBusMarshaller::beginMap(int kid, int vid)
qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. "
"Use qDBusRegisterMetaType to register it",
typeName, vid);
- error(QString::fromLatin1("Unregistered type %1 passed in arguments")
+ error(QLatin1String("Unregistered type %1 passed in arguments")
.arg(QLatin1String(typeName)));
return this;
}
@@ -417,7 +417,7 @@ bool QDBusMarshaller::appendVariantInternal(const QVariant &arg)
qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. "
"Use qDBusRegisterMetaType to register it",
QMetaType::typeName(id), id);
- error(QString::fromLatin1("Unregistered type %1 passed in arguments")
+ error(QLatin1String("Unregistered type %1 passed in arguments")
.arg(QLatin1String(QMetaType::typeName(id))));
return false;
}
diff --git a/src/dbus/qdbusmetaobject.cpp b/src/dbus/qdbusmetaobject.cpp
index c4296f5c55..806cf7b415 100644
--- a/src/dbus/qdbusmetaobject.cpp
+++ b/src/dbus/qdbusmetaobject.cpp
@@ -649,7 +649,7 @@ QDBusMetaObject *QDBusMetaObject::createMetaObject(const QString &interface, con
// mark as an error
error = QDBusError(QDBusError::UnknownInterface,
- QString::fromLatin1("Interface '%1' was not found")
+ QLatin1String("Interface '%1' was not found")
.arg(interface));
return 0;
}
diff --git a/src/dbus/qdbusmetatype.cpp b/src/dbus/qdbusmetatype.cpp
index 0729a3cbbb..58ce4f8930 100644
--- a/src/dbus/qdbusmetatype.cpp
+++ b/src/dbus/qdbusmetatype.cpp
@@ -93,7 +93,7 @@ void QDBusMetaTypeId::init()
// reentrancy is not a problem since everything else is locked on their own
// set the guard variable at the end
- if (!initialized.load()) {
+ if (!initialized.loadRelaxed()) {
// register our types with Qt Core (calling qMetaTypeId<T>() does this implicitly)
(void)message();
(void)argument();
@@ -145,7 +145,7 @@ void QDBusMetaTypeId::init()
qDBusRegisterMetaType<QVector<QDBusUnixFileDescriptor> >();
#endif
- initialized.store(true);
+ initialized.storeRelaxed(true);
}
}
diff --git a/src/dbus/qdbuspendingcall.cpp b/src/dbus/qdbuspendingcall.cpp
index 4d0131afff..7c2238900c 100644
--- a/src/dbus/qdbuspendingcall.cpp
+++ b/src/dbus/qdbuspendingcall.cpp
@@ -221,8 +221,8 @@ void QDBusPendingCallPrivate::checkReceivedSignature()
// can't use startsWith here because a null string doesn't start or end with an empty string
if (replyMessage.signature().indexOf(expectedReplySignature) != 0) {
- QString errorMsg = QLatin1String("Unexpected reply signature: got \"%1\", "
- "expected \"%2\"");
+ const auto errorMsg = QLatin1String("Unexpected reply signature: got \"%1\", "
+ "expected \"%2\"");
replyMessage = QDBusMessage::createError(
QDBusError::InvalidSignature,
errorMsg.arg(replyMessage.signature(), expectedReplySignature));
@@ -473,7 +473,7 @@ QDBusPendingCall QDBusPendingCall::fromCompletedCall(const QDBusMessage &msg)
msg.type() == QDBusMessage::ReplyMessage) {
d = new QDBusPendingCallPrivate(QDBusMessage(), 0);
d->replyMessage = msg;
- d->ref.store(1);
+ d->ref.storeRelaxed(1);
}
return QDBusPendingCall(d);
diff --git a/src/dbus/qdbusreply.cpp b/src/dbus/qdbusreply.cpp
index 6abfaf174c..cf1a70508c 100644
--- a/src/dbus/qdbusreply.cpp
+++ b/src/dbus/qdbusreply.cpp
@@ -228,14 +228,14 @@ void qDBusReplyFill(const QDBusMessage &reply, QDBusError &error, QVariant &data
receivedSignature = "<empty signature>";
QString errorMsg;
if (receivedType) {
- errorMsg = QString::fromLatin1("Unexpected reply signature: got \"%1\" (%4), "
+ errorMsg = QLatin1String("Unexpected reply signature: got \"%1\" (%4), "
"expected \"%2\" (%3)")
.arg(QLatin1String(receivedSignature),
QLatin1String(expectedSignature),
QLatin1String(data.typeName()),
QLatin1String(receivedType));
} else {
- errorMsg = QString::fromLatin1("Unexpected reply signature: got \"%1\", "
+ errorMsg = QLatin1String("Unexpected reply signature: got \"%1\", "
"expected \"%2\" (%3)")
.arg(QLatin1String(receivedSignature),
QLatin1String(expectedSignature),
diff --git a/src/dbus/qdbusserver.cpp b/src/dbus/qdbusserver.cpp
index a2dfb86164..2899fb7bea 100644
--- a/src/dbus/qdbusserver.cpp
+++ b/src/dbus/qdbusserver.cpp
@@ -117,7 +117,7 @@ QDBusServer::~QDBusServer()
d->serverConnectionNames.clear();
}
d->serverObject = nullptr;
- d->ref.store(0);
+ d->ref.storeRelaxed(0);
d->deleteLater();
}
diff --git a/src/dbus/qdbusunixfiledescriptor.cpp b/src/dbus/qdbusunixfiledescriptor.cpp
index 459cfc6c66..73d1db2680 100644
--- a/src/dbus/qdbusunixfiledescriptor.cpp
+++ b/src/dbus/qdbusunixfiledescriptor.cpp
@@ -208,7 +208,7 @@ QDBusUnixFileDescriptor::~QDBusUnixFileDescriptor()
*/
bool QDBusUnixFileDescriptor::isValid() const
{
- return d ? d->fd.load() != -1 : false;
+ return d ? d->fd.loadRelaxed() != -1 : false;
}
/*!
@@ -226,7 +226,7 @@ bool QDBusUnixFileDescriptor::isValid() const
*/
int QDBusUnixFileDescriptor::fileDescriptor() const
{
- return d ? d->fd.load() : -1;
+ return d ? d->fd.loadRelaxed() : -1;
}
// actual implementation
@@ -283,12 +283,12 @@ void QDBusUnixFileDescriptor::giveFileDescriptor(int fileDescriptor)
else
d = new QDBusUnixFileDescriptorPrivate;
- const int fdl = d->fd.load();
+ const int fdl = d->fd.loadRelaxed();
if (fdl != -1)
qt_safe_close(fdl);
if (fileDescriptor != -1)
- d->fd.store(fileDescriptor);
+ d->fd.storeRelaxed(fileDescriptor);
}
/*!
@@ -309,7 +309,7 @@ int QDBusUnixFileDescriptor::takeFileDescriptor()
QDBusUnixFileDescriptorPrivate::~QDBusUnixFileDescriptorPrivate()
{
- const int fdl = fd.load();
+ const int fdl = fd.loadRelaxed();
if (fdl != -1)
qt_safe_close(fdl);
}
diff --git a/src/dbus/qdbusutil_p.h b/src/dbus/qdbusutil_p.h
index 5a4b461194..0814cc8050 100644
--- a/src/dbus/qdbusutil_p.h
+++ b/src/dbus/qdbusutil_p.h
@@ -106,7 +106,7 @@ namespace Q_DBUS_NO_EXPORT QDBusUtil
return false;
}
if (isValidInterfaceName(name)) return true;
- *error = QDBusError(QDBusError::InvalidInterface, QString::fromLatin1("Invalid interface class: %1").arg(name));
+ *error = QDBusError(QDBusError::InvalidInterface, QLatin1String("Invalid interface class: %1").arg(name));
return false;
}
@@ -118,7 +118,7 @@ namespace Q_DBUS_NO_EXPORT QDBusUtil
return false;
}
if (isValidBusName(name)) return true;
- *error = QDBusError(QDBusError::InvalidService, QString::fromLatin1("Invalid service name: %1").arg(name));
+ *error = QDBusError(QDBusError::InvalidService, QLatin1String("Invalid service name: %1").arg(name));
return false;
}
@@ -130,7 +130,7 @@ namespace Q_DBUS_NO_EXPORT QDBusUtil
return false;
}
if (isValidObjectPath(path)) return true;
- *error = QDBusError(QDBusError::InvalidObjectPath, QString::fromLatin1("Invalid object path: %1").arg(path));
+ *error = QDBusError(QDBusError::InvalidObjectPath, QLatin1String("Invalid object path: %1").arg(path));
return false;
}
@@ -143,8 +143,8 @@ namespace Q_DBUS_NO_EXPORT QDBusUtil
return false;
}
if (isValidMemberName(name)) return true;
- *error = QDBusError(QDBusError::InvalidMember, QString::fromLatin1("Invalid %1 name: %2")
- .arg(QString::fromLatin1(nameType), name));
+ *error = QDBusError(QDBusError::InvalidMember, QLatin1String("Invalid %1 name: %2")
+ .arg(QLatin1String(nameType), name));
return false;
}
@@ -156,7 +156,7 @@ namespace Q_DBUS_NO_EXPORT QDBusUtil
return false;
}
if (isValidErrorName(name)) return true;
- *error = QDBusError(QDBusError::InvalidInterface, QString::fromLatin1("Invalid error name: %1").arg(name));
+ *error = QDBusError(QDBusError::InvalidInterface, QLatin1String("Invalid error name: %1").arg(name));
return false;
}
diff --git a/src/dbus/qdbusxmlgenerator.cpp b/src/dbus/qdbusxmlgenerator.cpp
index e3c503aa0f..c6b3b90508 100644
--- a/src/dbus/qdbusxmlgenerator.cpp
+++ b/src/dbus/qdbusxmlgenerator.cpp
@@ -108,14 +108,14 @@ static QString generateInterfaceXml(const QMetaObject *mo, int flags, int method
if (!signature)
continue;
- retval += QString::fromLatin1(" <property name=\"%1\" type=\"%2\" access=\"%3\"")
+ retval += QLatin1String(" <property name=\"%1\" type=\"%2\" access=\"%3\"")
.arg(QLatin1String(mp.name()),
QLatin1String(signature),
accessAsString(mp.isReadable(), mp.isWritable()));
if (QDBusMetaType::signatureToType(signature) == QVariant::Invalid) {
const char *typeName = QMetaType::typeName(typeId);
- retval += QString::fromLatin1(">\n <annotation name=\"org.qtproject.QtDBus.QtTypeName\" value=\"%3\"/>\n </property>\n")
+ retval += QLatin1String(">\n <annotation name=\"org.qtproject.QtDBus.QtTypeName\" value=\"%3\"/>\n </property>\n")
.arg(typeNameToXml(typeName));
} else {
retval += QLatin1String("/>\n");
@@ -157,12 +157,12 @@ static QString generateInterfaceXml(const QMetaObject *mo, int flags, int method
if (typeId != QMetaType::UnknownType && typeId != QMetaType::Void) {
const char *typeName = QDBusMetaType::typeToSignature(typeId);
if (typeName) {
- xml += QString::fromLatin1(" <arg type=\"%1\" direction=\"out\"/>\n")
+ xml += QLatin1String(" <arg type=\"%1\" direction=\"out\"/>\n")
.arg(typeNameToXml(typeName));
// do we need to describe this argument?
if (QDBusMetaType::signatureToType(typeName) == QVariant::Invalid)
- xml += QString::fromLatin1(" <annotation name=\"org.qtproject.QtDBus.QtTypeName.Out0\" value=\"%1\"/>\n")
+ xml += QLatin1String(" <annotation name=\"org.qtproject.QtDBus.QtTypeName.Out0\" value=\"%1\"/>\n")
.arg(typeNameToXml(QMetaType::typeName(typeId)));
} else {
qWarning() << "Unsupported return type" << typeId << QMetaType::typeName(typeId) << "in method" << mm.name();
@@ -199,7 +199,7 @@ static QString generateInterfaceXml(const QMetaObject *mo, int flags, int method
QString name;
if (!names.at(j - 1).isEmpty())
- name = QString::fromLatin1("name=\"%1\" ").arg(QLatin1String(names.at(j - 1)));
+ name = QLatin1String("name=\"%1\" ").arg(QLatin1String(names.at(j - 1)));
bool isOutput = isSignal || j > inputCount;
@@ -233,7 +233,7 @@ static QString generateInterfaceXml(const QMetaObject *mo, int flags, int method
" value=\"true\"/>\n");
retval += xml;
- retval += QString::fromLatin1(" </%1>\n")
+ retval += QLatin1String(" </%1>\n")
.arg(isSignal ? QLatin1String("signal") : QLatin1String("method"));
}
@@ -256,7 +256,7 @@ QString qDBusGenerateMetaObjectXml(QString interface, const QMetaObject *mo,
if (xml.isEmpty())
return QString(); // don't add an empty interface
- return QString::fromLatin1(" <interface name=\"%1\">\n%2 </interface>\n")
+ return QLatin1String(" <interface name=\"%1\">\n%2 </interface>\n")
.arg(interface, xml);
}
#if 0
diff --git a/src/gui/accessible/qaccessible.cpp b/src/gui/accessible/qaccessible.cpp
index 68e36de24b..db47a3abc1 100644
--- a/src/gui/accessible/qaccessible.cpp
+++ b/src/gui/accessible/qaccessible.cpp
@@ -350,6 +350,7 @@ Q_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core");
\value MenuItem An item in a menu or menu bar.
\value NoRole The object has no role. This usually indicates an invalid object.
\value Note A section whose content is parenthetic or ancillary to the main content of the resource.
+ \value Notification An object that represents a notification (e.g. in the system tray). This role only has an effect on Linux.
\value PageTab A page tab that the user can select to switch to a different page in a dialog.
\value PageTabList A list of page tabs.
\value Paragraph A paragraph of text (usually found in documents).
diff --git a/src/gui/accessible/qaccessible.h b/src/gui/accessible/qaccessible.h
index e2163b06d0..2220efd5cb 100644
--- a/src/gui/accessible/qaccessible.h
+++ b/src/gui/accessible/qaccessible.h
@@ -299,6 +299,7 @@ public:
Paragraph = 0x00000083,
WebDocument = 0x00000084,
Section = 0x00000085,
+ Notification = 0x00000086,
// IAccessible2 roles
// IA2_ROLE_CANVAS = 0x401, ### Qt 6 use this one instead of Canvas above
diff --git a/src/gui/doc/snippets/code/doc_src_coordsys.cpp b/src/gui/doc/snippets/code/doc_src_coordsys.cpp
index 7c53d9e731..147c146b44 100644
--- a/src/gui/doc/snippets/code/doc_src_coordsys.cpp
+++ b/src/gui/doc/snippets/code/doc_src_coordsys.cpp
@@ -52,6 +52,7 @@
QPainter painter(this);
painter.setPen(Qt::darkGreen);
+// Using the (x y w h) overload
painter.drawRect(1, 2, 6, 4);
//! [0]
@@ -69,6 +70,7 @@ QPainter painter(this);
painter.setRenderHint(
QPainter::Antialiasing);
painter.setPen(Qt::darkGreen);
+// Using the (x y w h) overload
painter.drawRect(1, 2, 6, 4);
//! [2]
diff --git a/src/gui/doc/src/coordsys.qdoc b/src/gui/doc/src/coordsys.qdoc
index 1856428361..1018b2122b 100644
--- a/src/gui/doc/src/coordsys.qdoc
+++ b/src/gui/doc/src/coordsys.qdoc
@@ -70,8 +70,15 @@
\li \inlineimage coordinatesystem-rect.png
\li \inlineimage coordinatesystem-line.png
\row
- \li QRect(1, 2, 6, 4)
+ \li QRect(QPoint(1, 2), QPoint(7, 6))
+ \li QLine(QPoint(2, 7), QPoint(6, 1))
+ \row
+ \li
\li QLine(2, 7, 6, 1)
+ \row
+ \li QRect(QPoint(1, 2), QSize(6, 4))
+ \row
+ \li QRect(1, 2, 6, 4)
\endtable
\section2 Aliased Painting
diff --git a/src/gui/doc/src/dontdocument.qdoc b/src/gui/doc/src/dontdocument.qdoc
new file mode 100644
index 0000000000..b360acefc1
--- /dev/null
+++ b/src/gui/doc/src/dontdocument.qdoc
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \dontdocument (QTypeInfo QScreenOrientationChangeEvent QApplicationStateChangeEvent
+ QImageTextKeyLang QMetaTypeId QAbstractUndoItem
+ QOpenGLVersionStatus
+ QOpenGLVersionFunctionsBackend
+ QOpenGLVersionFunctionsStorage
+ QOpenGLFunctions_1_0_CoreBackend
+ QOpenGLFunctions_1_1_CoreBackend
+ QOpenGLFunctions_1_2_CoreBackend
+ QOpenGLFunctions_1_3_CoreBackend
+ QOpenGLFunctions_1_4_CoreBackend
+ QOpenGLFunctions_1_5_CoreBackend
+ QOpenGLFunctions_2_0_CoreBackend
+ QOpenGLFunctions_2_1_CoreBackend
+ QOpenGLFunctions_3_0_CoreBackend
+ QOpenGLFunctions_3_1_CoreBackend
+ QOpenGLFunctions_3_2_CoreBackend
+ QOpenGLFunctions_3_3_CoreBackend
+ QOpenGLFunctions_4_0_CoreBackend
+ QOpenGLFunctions_4_1_CoreBackend
+ QOpenGLFunctions_4_2_CoreBackend
+ QOpenGLFunctions_4_3_CoreBackend
+ QOpenGLFunctions_4_4_CoreBackend
+ QOpenGLFunctions_4_5_CoreBackend
+ QOpenGLFunctions_1_0_DeprecatedBackend
+ QOpenGLFunctions_1_1_DeprecatedBackend
+ QOpenGLFunctions_1_2_DeprecatedBackend
+ QOpenGLFunctions_1_3_DeprecatedBackend
+ QOpenGLFunctions_1_4_DeprecatedBackend
+ QOpenGLFunctions_2_0_DeprecatedBackend
+ QOpenGLFunctions_3_0_DeprecatedBackend
+ QOpenGLFunctions_3_3_DeprecatedBackend
+ QOpenGLFunctions_4_5_DeprecatedBackend
+ QTextFrameLayoutData QPlatformDropQtResponse QPlatformDragQtResponse
+ QPlatformOffscreenSurface QColorDialogOptions QFontDialogOptions
+ QFileDialogOptions QMessageDialogOptions QMessageDialogOptions::CustomButton
+ QPlatformSessionManager QPlatformIntegrationPlugin QPlatformMenuItem
+ QPlatformMenu QPlatformMenuBar QPlatformTextureList)
+*/
diff --git a/src/gui/gui.pro b/src/gui/gui.pro
index edf8124081..45c8c05162 100644
--- a/src/gui/gui.pro
+++ b/src/gui/gui.pro
@@ -49,6 +49,7 @@ qtConfig(animation): include(animation/animation.pri)
include(itemmodels/itemmodels.pri)
include(vulkan/vulkan.pri)
include(platform/platform.pri)
+include(rhi/rhi.pri)
QMAKE_LIBS += $$QMAKE_LIBS_GUI
diff --git a/src/gui/image/qicon.cpp b/src/gui/image/qicon.cpp
index 1d1b6d3c49..df8220a0c6 100644
--- a/src/gui/image/qicon.cpp
+++ b/src/gui/image/qicon.cpp
@@ -987,7 +987,7 @@ bool QIcon::isNull() const
*/
bool QIcon::isDetached() const
{
- return !d || d->ref.load() == 1;
+ return !d || d->ref.loadRelaxed() == 1;
}
/*! \internal
@@ -1000,7 +1000,7 @@ void QIcon::detach()
delete d;
d = 0;
return;
- } else if (d->ref.load() != 1) {
+ } else if (d->ref.loadRelaxed() != 1) {
QIconPrivate *x = new QIconPrivate(d->engine->clone());
if (!d->ref.deref())
delete d;
diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp
index 917dde3b0f..61d32b0dec 100644
--- a/src/gui/image/qimage.cpp
+++ b/src/gui/image/qimage.cpp
@@ -1082,10 +1082,10 @@ QImage::operator QVariant() const
void QImage::detach()
{
if (d) {
- if (d->is_cached && d->ref.load() == 1)
+ if (d->is_cached && d->ref.loadRelaxed() == 1)
QImagePixmapCleanupHooks::executeImageHooks(cacheKey());
- if (d->ref.load() != 1 || d->ro_data)
+ if (d->ref.loadRelaxed() != 1 || d->ro_data)
*this = copy();
if (d)
@@ -4422,7 +4422,7 @@ qint64 QImage::cacheKey() const
bool QImage::isDetached() const
{
- return d && d->ref.load() == 1;
+ return d && d->ref.loadRelaxed() == 1;
}
@@ -5087,7 +5087,7 @@ bool QImageData::convertInPlace(QImage::Format newFormat, Qt::ImageConversionFla
return true;
// No in-place conversion if we have to detach
- if (ref.load() > 1 || !own_data)
+ if (ref.loadRelaxed() > 1 || !own_data)
return false;
InPlace_Image_Converter converter = qimage_inplace_converter_map[format][newFormat];
diff --git a/src/gui/image/qimagereader.cpp b/src/gui/image/qimagereader.cpp
index ae433ff651..2762b702b2 100644
--- a/src/gui/image/qimagereader.cpp
+++ b/src/gui/image/qimagereader.cpp
@@ -1321,10 +1321,12 @@ bool QImageReader::read(QImage *image)
}
}
- // successful read; check for "@2x" file name suffix and set device pixel ratio.
- static bool disable2xImageLoading = !qEnvironmentVariableIsEmpty("QT_HIGHDPI_DISABLE_2X_IMAGE_LOADING");
- if (!disable2xImageLoading && QFileInfo(fileName()).baseName().endsWith(QLatin1String("@2x"))) {
- image->setDevicePixelRatio(2.0);
+ // successful read; check for "@Nx" file name suffix and set device pixel ratio.
+ static bool disableNxImageLoading = !qEnvironmentVariableIsEmpty("QT_HIGHDPI_DISABLE_2X_IMAGE_LOADING");
+ if (!disableNxImageLoading) {
+ const QByteArray suffix = QFileInfo(fileName()).baseName().right(3).toLatin1();
+ if (suffix.length() == 3 && suffix[0] == '@' && suffix[1] >= '2' && suffix[1] <= '9' && suffix[2] == 'x')
+ image->setDevicePixelRatio(suffix[1] - '0');
}
if (autoTransform())
qt_imageTransform(*image, transformation());
diff --git a/src/gui/image/qmovie.cpp b/src/gui/image/qmovie.cpp
index 79203c7b98..4b588527ae 100644
--- a/src/gui/image/qmovie.cpp
+++ b/src/gui/image/qmovie.cpp
@@ -175,11 +175,12 @@
#include "qmovie.h"
#include "qglobal.h"
+#include "qelapsedtimer.h"
#include "qimage.h"
#include "qimagereader.h"
#include "qpixmap.h"
#include "qrect.h"
-#include "qdatetime.h"
+#include "qelapsedtimer.h"
#include "qtimer.h"
#include "qpair.h"
#include "qmap.h"
@@ -437,7 +438,7 @@ QFrameInfo QMoviePrivate::infoForFrame(int frameNumber)
*/
bool QMoviePrivate::next()
{
- QTime time;
+ QElapsedTimer time;
time.start();
QFrameInfo info = infoForFrame(nextFrameNumber);
if (!info.isValid())
diff --git a/src/gui/image/qpicture.cpp b/src/gui/image/qpicture.cpp
index fe1e4d2c9b..8548f1857e 100644
--- a/src/gui/image/qpicture.cpp
+++ b/src/gui/image/qpicture.cpp
@@ -235,7 +235,7 @@ void QPicture::detach()
bool QPicture::isDetached() const
{
- return d_func()->ref.load() == 1;
+ return d_func()->ref.loadRelaxed() == 1;
}
/*!
diff --git a/src/gui/image/qpixmap.cpp b/src/gui/image/qpixmap.cpp
index 399ad7453d..b6e41f16a5 100644
--- a/src/gui/image/qpixmap.cpp
+++ b/src/gui/image/qpixmap.cpp
@@ -262,7 +262,7 @@ QPixmap::QPixmap(const char * const xpm[])
QPixmap::~QPixmap()
{
- Q_ASSERT(!data || data->ref.load() >= 1); // Catch if ref-counting changes again
+ Q_ASSERT(!data || data->ref.loadRelaxed() >= 1); // Catch if ref-counting changes again
}
/*!
@@ -910,7 +910,7 @@ void QPixmap::fill(const QColor &color)
return;
}
- if (data->ref.load() == 1) {
+ if (data->ref.loadRelaxed() == 1) {
// detach() will also remove this pixmap from caches, so
// it has to be called even when ref == 1.
detach();
@@ -1053,7 +1053,7 @@ QDataStream &operator>>(QDataStream &stream, QPixmap &pixmap)
bool QPixmap::isDetached() const
{
- return data && data->ref.load() == 1;
+ return data && data->ref.loadRelaxed() == 1;
}
/*!
@@ -1523,10 +1523,10 @@ void QPixmap::detach()
rasterData->image.detach();
}
- if (data->is_cached && data->ref.load() == 1)
+ if (data->is_cached && data->ref.loadRelaxed() == 1)
QImagePixmapCleanupHooks::executePlatformPixmapModificationHooks(data.data());
- if (data->ref.load() != 1) {
+ if (data->ref.loadRelaxed() != 1) {
*this = copy();
}
++data->detach_no;
diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp
index 2f3a02f4d6..e1d685b1b0 100644
--- a/src/gui/kernel/qevent.cpp
+++ b/src/gui/kernel/qevent.cpp
@@ -97,35 +97,35 @@ QEnterEvent::~QEnterEvent()
/*!
\fn QPoint QEnterEvent::globalPos() const
- Returns the global position of the widget \e{at the time of the event}.
+ Returns the global position of the mouse cursor \e{at the time of the event}.
*/
/*!
\fn int QEnterEvent::globalX() const
- Returns the global position on the X-axis of the mouse cursor relative to the the widget.
+ Returns the global position on the X-axis of the mouse cursor \e{at the time of the event}.
*/
/*!
\fn int QEnterEvent::globalY() const
- Returns the global position on the Y-axis of the mouse cursor relative to the the widget.
+ Returns the global position on the Y-axis of the mouse cursor \e{at the time of the event}.
*/
/*!
- \fn QPoint QEnterEvent::localPos() const
+ \fn QPointF QEnterEvent::localPos() const
Returns the mouse cursor's position relative to the receiving widget.
*/
/*!
\fn QPoint QEnterEvent::pos() const
- Returns the position of the mouse cursor in global screen coordinates.
+ Returns the position of the mouse cursor relative to the receiving widget.
*/
/*!
- \fn QPoint QEnterEvent::screenPos() const
+ \fn QPointF QEnterEvent::screenPos() const
Returns the position of the mouse cursor relative to the receiving screen.
*/
/*!
- \fn QPoint QEnterEvent::windowPos() const
+ \fn QPointF QEnterEvent::windowPos() const
Returns the position of the mouse cursor relative to the receiving window.
*/
@@ -4921,7 +4921,7 @@ QVector<QPointF> QTouchEvent::TouchPoint::rawScreenPositions() const
/*! \internal */
void QTouchEvent::TouchPoint::setId(int id)
{
- if (d->ref.load() != 1)
+ if (d->ref.loadRelaxed() != 1)
d = d->detach();
d->id = id;
}
@@ -4929,7 +4929,7 @@ void QTouchEvent::TouchPoint::setId(int id)
/*! \internal */
void QTouchEvent::TouchPoint::setUniqueId(qint64 uid)
{
- if (d->ref.load() != 1)
+ if (d->ref.loadRelaxed() != 1)
d = d->detach();
d->uniqueId = QPointingDeviceUniqueId::fromNumericId(uid);
}
@@ -4937,7 +4937,7 @@ void QTouchEvent::TouchPoint::setUniqueId(qint64 uid)
/*! \internal */
void QTouchEvent::TouchPoint::setState(Qt::TouchPointStates state)
{
- if (d->ref.load() != 1)
+ if (d->ref.loadRelaxed() != 1)
d = d->detach();
d->state = state;
}
@@ -4945,7 +4945,7 @@ void QTouchEvent::TouchPoint::setState(Qt::TouchPointStates state)
/*! \internal */
void QTouchEvent::TouchPoint::setPos(const QPointF &pos)
{
- if (d->ref.load() != 1)
+ if (d->ref.loadRelaxed() != 1)
d = d->detach();
d->pos = pos;
}
@@ -4953,7 +4953,7 @@ void QTouchEvent::TouchPoint::setPos(const QPointF &pos)
/*! \internal */
void QTouchEvent::TouchPoint::setScenePos(const QPointF &scenePos)
{
- if (d->ref.load() != 1)
+ if (d->ref.loadRelaxed() != 1)
d = d->detach();
d->scenePos = scenePos;
}
@@ -4961,7 +4961,7 @@ void QTouchEvent::TouchPoint::setScenePos(const QPointF &scenePos)
/*! \internal */
void QTouchEvent::TouchPoint::setScreenPos(const QPointF &screenPos)
{
- if (d->ref.load() != 1)
+ if (d->ref.loadRelaxed() != 1)
d = d->detach();
d->screenPos = screenPos;
}
@@ -4969,7 +4969,7 @@ void QTouchEvent::TouchPoint::setScreenPos(const QPointF &screenPos)
/*! \internal */
void QTouchEvent::TouchPoint::setNormalizedPos(const QPointF &normalizedPos)
{
- if (d->ref.load() != 1)
+ if (d->ref.loadRelaxed() != 1)
d = d->detach();
d->normalizedPos = normalizedPos;
}
@@ -4977,7 +4977,7 @@ void QTouchEvent::TouchPoint::setNormalizedPos(const QPointF &normalizedPos)
/*! \internal */
void QTouchEvent::TouchPoint::setStartPos(const QPointF &startPos)
{
- if (d->ref.load() != 1)
+ if (d->ref.loadRelaxed() != 1)
d = d->detach();
d->startPos = startPos;
}
@@ -4985,7 +4985,7 @@ void QTouchEvent::TouchPoint::setStartPos(const QPointF &startPos)
/*! \internal */
void QTouchEvent::TouchPoint::setStartScenePos(const QPointF &startScenePos)
{
- if (d->ref.load() != 1)
+ if (d->ref.loadRelaxed() != 1)
d = d->detach();
d->startScenePos = startScenePos;
}
@@ -4993,7 +4993,7 @@ void QTouchEvent::TouchPoint::setStartScenePos(const QPointF &startScenePos)
/*! \internal */
void QTouchEvent::TouchPoint::setStartScreenPos(const QPointF &startScreenPos)
{
- if (d->ref.load() != 1)
+ if (d->ref.loadRelaxed() != 1)
d = d->detach();
d->startScreenPos = startScreenPos;
}
@@ -5001,7 +5001,7 @@ void QTouchEvent::TouchPoint::setStartScreenPos(const QPointF &startScreenPos)
/*! \internal */
void QTouchEvent::TouchPoint::setStartNormalizedPos(const QPointF &startNormalizedPos)
{
- if (d->ref.load() != 1)
+ if (d->ref.loadRelaxed() != 1)
d = d->detach();
d->startNormalizedPos = startNormalizedPos;
}
@@ -5009,7 +5009,7 @@ void QTouchEvent::TouchPoint::setStartNormalizedPos(const QPointF &startNormaliz
/*! \internal */
void QTouchEvent::TouchPoint::setLastPos(const QPointF &lastPos)
{
- if (d->ref.load() != 1)
+ if (d->ref.loadRelaxed() != 1)
d = d->detach();
d->lastPos = lastPos;
}
@@ -5017,7 +5017,7 @@ void QTouchEvent::TouchPoint::setLastPos(const QPointF &lastPos)
/*! \internal */
void QTouchEvent::TouchPoint::setLastScenePos(const QPointF &lastScenePos)
{
- if (d->ref.load() != 1)
+ if (d->ref.loadRelaxed() != 1)
d = d->detach();
d->lastScenePos = lastScenePos;
}
@@ -5025,7 +5025,7 @@ void QTouchEvent::TouchPoint::setLastScenePos(const QPointF &lastScenePos)
/*! \internal */
void QTouchEvent::TouchPoint::setLastScreenPos(const QPointF &lastScreenPos)
{
- if (d->ref.load() != 1)
+ if (d->ref.loadRelaxed() != 1)
d = d->detach();
d->lastScreenPos = lastScreenPos;
}
@@ -5033,7 +5033,7 @@ void QTouchEvent::TouchPoint::setLastScreenPos(const QPointF &lastScreenPos)
/*! \internal */
void QTouchEvent::TouchPoint::setLastNormalizedPos(const QPointF &lastNormalizedPos)
{
- if (d->ref.load() != 1)
+ if (d->ref.loadRelaxed() != 1)
d = d->detach();
d->lastNormalizedPos = lastNormalizedPos;
}
@@ -5044,7 +5044,7 @@ void QTouchEvent::TouchPoint::setLastNormalizedPos(const QPointF &lastNormalized
*/
void QTouchEvent::TouchPoint::setRect(const QRectF &rect)
{
- if (d->ref.load() != 1)
+ if (d->ref.loadRelaxed() != 1)
d = d->detach();
d->pos = rect.center();
d->ellipseDiameters = rect.size();
@@ -5055,7 +5055,7 @@ void QTouchEvent::TouchPoint::setRect(const QRectF &rect)
*/
void QTouchEvent::TouchPoint::setSceneRect(const QRectF &sceneRect)
{
- if (d->ref.load() != 1)
+ if (d->ref.loadRelaxed() != 1)
d = d->detach();
d->scenePos = sceneRect.center();
d->ellipseDiameters = sceneRect.size();
@@ -5066,7 +5066,7 @@ void QTouchEvent::TouchPoint::setSceneRect(const QRectF &sceneRect)
*/
void QTouchEvent::TouchPoint::setScreenRect(const QRectF &screenRect)
{
- if (d->ref.load() != 1)
+ if (d->ref.loadRelaxed() != 1)
d = d->detach();
d->screenPos = screenRect.center();
d->ellipseDiameters = screenRect.size();
@@ -5075,7 +5075,7 @@ void QTouchEvent::TouchPoint::setScreenRect(const QRectF &screenRect)
/*! \internal */
void QTouchEvent::TouchPoint::setPressure(qreal pressure)
{
- if (d->ref.load() != 1)
+ if (d->ref.loadRelaxed() != 1)
d = d->detach();
d->pressure = pressure;
}
@@ -5083,7 +5083,7 @@ void QTouchEvent::TouchPoint::setPressure(qreal pressure)
/*! \internal */
void QTouchEvent::TouchPoint::setRotation(qreal angle)
{
- if (d->ref.load() != 1)
+ if (d->ref.loadRelaxed() != 1)
d = d->detach();
d->rotation = angle;
}
@@ -5091,7 +5091,7 @@ void QTouchEvent::TouchPoint::setRotation(qreal angle)
/*! \internal */
void QTouchEvent::TouchPoint::setEllipseDiameters(const QSizeF &dia)
{
- if (d->ref.load() != 1)
+ if (d->ref.loadRelaxed() != 1)
d = d->detach();
d->ellipseDiameters = dia;
}
@@ -5099,7 +5099,7 @@ void QTouchEvent::TouchPoint::setEllipseDiameters(const QSizeF &dia)
/*! \internal */
void QTouchEvent::TouchPoint::setVelocity(const QVector2D &v)
{
- if (d->ref.load() != 1)
+ if (d->ref.loadRelaxed() != 1)
d = d->detach();
d->velocity = v;
}
@@ -5107,7 +5107,7 @@ void QTouchEvent::TouchPoint::setVelocity(const QVector2D &v)
/*! \internal */
void QTouchEvent::TouchPoint::setRawScreenPositions(const QVector<QPointF> &positions)
{
- if (d->ref.load() != 1)
+ if (d->ref.loadRelaxed() != 1)
d = d->detach();
d->rawScreenPositions = positions;
}
@@ -5117,7 +5117,7 @@ void QTouchEvent::TouchPoint::setRawScreenPositions(const QVector<QPointF> &posi
*/
void QTouchEvent::TouchPoint::setFlags(InfoFlags flags)
{
- if (d->ref.load() != 1)
+ if (d->ref.loadRelaxed() != 1)
d = d->detach();
d->flags = flags;
}
diff --git a/src/gui/kernel/qevent_p.h b/src/gui/kernel/qevent_p.h
index 7df4a1e25b..c2d8bd72b9 100644
--- a/src/gui/kernel/qevent_p.h
+++ b/src/gui/kernel/qevent_p.h
@@ -73,7 +73,7 @@ public:
inline QTouchEventTouchPointPrivate *detach()
{
QTouchEventTouchPointPrivate *d = new QTouchEventTouchPointPrivate(*this);
- d->ref.store(1);
+ d->ref.storeRelaxed(1);
if (!this->ref.deref())
delete this;
return d;
diff --git a/src/gui/kernel/qguiapplication_p.h b/src/gui/kernel/qguiapplication_p.h
index 6902a99eca..2ecde0354a 100644
--- a/src/gui/kernel/qguiapplication_p.h
+++ b/src/gui/kernel/qguiapplication_p.h
@@ -114,7 +114,7 @@ public:
static QAbstractEventDispatcher *qt_qpa_core_dispatcher()
{
if (QCoreApplication::instance())
- return QCoreApplication::instance()->d_func()->threadData->eventDispatcher.load();
+ return QCoreApplication::instance()->d_func()->threadData->eventDispatcher.loadRelaxed();
else
return nullptr;
}
diff --git a/src/gui/kernel/qhighdpiscaling.cpp b/src/gui/kernel/qhighdpiscaling.cpp
index 4f8e9a3817..93fcb1a216 100644
--- a/src/gui/kernel/qhighdpiscaling.cpp
+++ b/src/gui/kernel/qhighdpiscaling.cpp
@@ -400,7 +400,7 @@ QPoint QHighDpiScaling::mapPositionToGlobal(const QPoint &pos, const QPoint &win
QPoint QHighDpiScaling::mapPositionFromGlobal(const QPoint &pos, const QPoint &windowGlobalPosition, const QWindow *window)
{
QPoint windowPosCandidate = pos - windowGlobalPosition;
- if (QGuiApplicationPrivate::screen_list.size() <= 1)
+ if (QGuiApplicationPrivate::screen_list.size() <= 1 || window->handle() == nullptr)
return windowPosCandidate;
// Device independent global (screen) space may discontiguous when high-dpi scaling
diff --git a/src/gui/kernel/qhighdpiscaling_p.h b/src/gui/kernel/qhighdpiscaling_p.h
index 91cfbcbfc7..6e89f85746 100644
--- a/src/gui/kernel/qhighdpiscaling_p.h
+++ b/src/gui/kernel/qhighdpiscaling_p.h
@@ -108,208 +108,114 @@ private:
namespace QHighDpi {
-inline QPointF fromNative(const QPointF &pos, qreal scaleFactor, const QPointF &origin)
+template <typename T>
+inline T scale(const T &value, qreal scaleFactor, QPoint origin = QPoint(0, 0))
{
- return (pos - origin) / scaleFactor + origin;
+ Q_UNUSED(origin)
+ return value * scaleFactor;
}
-inline QPointF toNative(const QPointF &pos, qreal scaleFactor, const QPointF &origin)
+inline QPointF scale(const QPointF &pos, qreal scaleFactor, QPointF origin = QPointF(0, 0))
{
return (pos - origin) * scaleFactor + origin;
}
-inline QPoint fromNative(const QPoint &pos, qreal scaleFactor, const QPoint &origin)
-{
- return (pos - origin) / scaleFactor + origin;
-}
-
-inline QPoint toNative(const QPoint &pos, qreal scaleFactor, const QPoint &origin)
+inline QPoint scale(const QPoint &pos, qreal scaleFactor, QPoint origin = QPoint(0, 0))
{
return (pos - origin) * scaleFactor + origin;
}
-inline QPoint fromNative(const QPoint &pos, qreal scaleFactor)
-{
- return pos / scaleFactor;
-}
-
-inline QPoint toNative(const QPoint &pos, qreal scaleFactor)
-{
- return pos * scaleFactor;
-}
-
-inline QSize fromNative(const QSize &size, qreal scaleFactor)
-{
- return size / scaleFactor; // TODO: should we round up?
-}
-
-inline QSize toNative(const QSize &size, qreal scaleFactor)
+inline QRect scale(const QRect &rect, qreal scaleFactor, QPoint origin = QPoint(0, 0))
{
- return size * scaleFactor;
-}
-
-inline QSizeF fromNative(const QSizeF &size, qreal scaleFactor)
-{
- return size / scaleFactor;
-}
-
-inline QSizeF toNative(const QSizeF &size, qreal scaleFactor)
-{
- return size * scaleFactor;
-}
-
-inline QRect fromNative(const QRect &rect, qreal scaleFactor, const QPoint &origin)
-{
- return QRect(fromNative(rect.topLeft(), scaleFactor, origin), fromNative(rect.size(), scaleFactor));
-}
-
-inline QRect toNative(const QRect &rect, qreal scaleFactor, const QPoint &origin)
-{
- return QRect(toNative(rect.topLeft(), scaleFactor, origin), toNative(rect.size(), scaleFactor));
-
-}
-
-inline QRect fromNative(const QRect &rect, const QScreen *screen, const QPoint &screenOrigin)
-{
- return fromNative(rect, QHighDpiScaling::factor(screen), screenOrigin);
-}
-
-inline QRect fromNativeScreenGeometry(const QRect &nativeScreenGeometry, const QScreen *screen)
-{
- return QRect(nativeScreenGeometry.topLeft(),
- fromNative(nativeScreenGeometry.size(), QHighDpiScaling::factor(screen)));
-}
-
-inline QPoint fromNativeLocalPosition(const QPoint &pos, const QWindow *window)
-{
- const qreal scaleFactor = QHighDpiScaling::factor(window);
- return pos / scaleFactor;
-}
-
-inline QPoint toNativeLocalPosition(const QPoint &pos, const QWindow *window)
-{
- const qreal scaleFactor = QHighDpiScaling::factor(window);
- return pos * scaleFactor;
-}
-
-inline QPointF fromNativeLocalPosition(const QPointF &pos, const QWindow *window)
-{
- const qreal scaleFactor = QHighDpiScaling::factor(window);
- return pos / scaleFactor;
-}
-
-inline QPointF toNativeLocalPosition(const QPointF &pos, const QWindow *window)
-{
- const qreal scaleFactor = QHighDpiScaling::factor(window);
- return pos * scaleFactor;
+ return QRect(scale(rect.topLeft(), scaleFactor, origin), scale(rect.size(), scaleFactor));
}
-template <typename C>
-inline QRect fromNativePixels(const QRect &pixelRect, const C *context)
+inline QRectF scale(const QRectF &rect, qreal scaleFactor, QPoint origin = QPoint(0, 0))
{
- const qreal scaleFactor = QHighDpiScaling::factor(context);
- const QPoint origin = QHighDpiScaling::origin(context);
- return QRect(fromNative(pixelRect.topLeft(), scaleFactor, origin),
- fromNative(pixelRect.size(), scaleFactor));
+ return QRectF(scale(rect.topLeft(), scaleFactor, origin), scale(rect.size(), scaleFactor));
}
-template <typename C>
-inline QRect toNativePixels(const QRect &pointRect, const C *context)
+inline QMargins scale(const QMargins &margins, qreal scaleFactor, QPoint origin = QPoint(0, 0))
{
- const qreal scaleFactor = QHighDpiScaling::factor(context);
- const QPoint origin = QHighDpiScaling::origin(context);
- return QRect(toNative(pointRect.topLeft(), scaleFactor, origin),
- toNative(pointRect.size(), scaleFactor));
+ Q_UNUSED(origin)
+ return QMargins(qRound(qreal(margins.left()) * scaleFactor), qRound(qreal(margins.top()) * scaleFactor),
+ qRound(qreal(margins.right()) * scaleFactor), qRound(qreal(margins.bottom()) * scaleFactor));
}
-template <typename C>
-inline QRectF toNativePixels(const QRectF &pointRect, const C *context)
+template <typename T>
+QVector<T> scale(const QVector<T> &vector, qreal scaleFactor, QPoint origin = QPoint(0, 0))
{
- const qreal scaleFactor = QHighDpiScaling::factor(context);
- const QPoint origin = QHighDpiScaling::origin(context);
- return QRectF(toNative(pointRect.topLeft(), scaleFactor, origin),
- toNative(pointRect.size(), scaleFactor));
-}
+ if (!QHighDpiScaling::isActive())
+ return vector;
-template <typename C>
-inline QRectF fromNativePixels(const QRectF &pixelRect, const C *context)
-{
- const qreal scaleFactor = QHighDpiScaling::factor(context);
- const QPoint origin = QHighDpiScaling::origin(context);
- return QRectF(fromNative(pixelRect.topLeft(), scaleFactor, origin),
- fromNative(pixelRect.size(), scaleFactor));
+ QVector<T> scaled;
+ scaled.reserve(vector.size());
+ for (const T &item : vector)
+ scaled.append(scale(item, scaleFactor, origin));
+ return scaled;
}
-inline QSize fromNativePixels(const QSize &pixelSize, const QWindow *window)
+inline QRegion scale(const QRegion &region, qreal scaleFactor, QPoint origin = QPoint(0, 0))
{
- return pixelSize / QHighDpiScaling::factor(window);
-}
+ if (!QHighDpiScaling::isActive())
+ return region;
-inline QSize toNativePixels(const QSize &pointSize, const QWindow *window)
-{
- return pointSize * QHighDpiScaling::factor(window);
+ QRegion scaled;
+ for (const QRect &rect : region)
+ scaled += scale(rect, scaleFactor, origin);
+ return scaled;
}
-inline QSizeF fromNativePixels(const QSizeF &pixelSize, const QWindow *window)
+template <typename T, typename C>
+T fromNativePixels(const T &value, const C *context)
{
- return pixelSize / QHighDpiScaling::factor(window);
+ return scale(value, qreal(1) / QHighDpiScaling::factor(context), QHighDpiScaling::origin(context));
}
-inline QSizeF toNativePixels(const QSizeF &pointSize, const QWindow *window)
+template <typename T, typename C>
+T toNativePixels(const T &value, const C *context)
{
- return pointSize * QHighDpiScaling::factor(window);
+ return scale(value, QHighDpiScaling::factor(context), QHighDpiScaling::origin(context));
}
-template <typename C>
-inline QPoint fromNativePixels(const QPoint &pixelPoint, const C *context)
+template <typename T, typename C>
+T fromNativeLocalPosition(const T &value, const C *context)
{
- return fromNative(pixelPoint, QHighDpiScaling::factor(context), QHighDpiScaling::origin(context));
+ return scale(value, qreal(1) / QHighDpiScaling::factor(context));
}
-template <typename C>
-inline QPoint toNativePixels(const QPoint &pointPoint, const C *context)
+template <typename T, typename C>
+T toNativeLocalPosition(const T &value, const C *context)
{
- return toNative(pointPoint, QHighDpiScaling::factor(context), QHighDpiScaling::origin(context));
+ return scale(value, QHighDpiScaling::factor(context));
}
-template <typename C>
-inline QPointF fromNativePixels(const QPointF &pixelPoint, const C *context)
+template <typename T>
+inline T fromNative(const T &value, qreal scaleFactor, QPoint origin = QPoint(0, 0))
{
- return fromNative(pixelPoint, QHighDpiScaling::factor(context), QHighDpiScaling::origin(context));
+ return scale(value, qreal(1) / scaleFactor, origin);
}
-template <typename C>
-inline QPointF toNativePixels(const QPointF &pointPoint, const C *context)
+template <typename T>
+inline T toNative(const T &value, qreal scaleFactor, QPoint origin = QPoint(0, 0))
{
- return toNative(pointPoint, QHighDpiScaling::factor(context), QHighDpiScaling::origin(context));
+ return scale(value, scaleFactor, origin);
}
-inline QMargins fromNativePixels(const QMargins &pixelMargins, const QWindow *window)
+inline QRect fromNative(const QRect &rect, const QScreen *screen, const QPoint &screenOrigin)
{
- const qreal scaleFactor = QHighDpiScaling::factor(window);
- return QMargins(pixelMargins.left() / scaleFactor, pixelMargins.top() / scaleFactor,
- pixelMargins.right() / scaleFactor, pixelMargins.bottom() / scaleFactor);
+ return scale(rect, qreal(1) / QHighDpiScaling::factor(screen), screenOrigin);
}
-inline QMargins toNativePixels(const QMargins &pointMargins, const QWindow *window)
+inline QRect fromNativeScreenGeometry(const QRect &nativeScreenGeometry, const QScreen *screen)
{
- const qreal scaleFactor = QHighDpiScaling::factor(window);
- return QMargins(pointMargins.left() * scaleFactor, pointMargins.top() * scaleFactor,
- pointMargins.right() * scaleFactor, pointMargins.bottom() * scaleFactor);
+ return QRect(nativeScreenGeometry.topLeft(),
+ scale(nativeScreenGeometry.size(), qreal(1) / QHighDpiScaling::factor(screen)));
}
inline QRegion fromNativeLocalRegion(const QRegion &pixelRegion, const QWindow *window)
{
- if (!QHighDpiScaling::isActive())
- return pixelRegion;
-
- qreal scaleFactor = QHighDpiScaling::factor(window);
- QRegion pointRegion;
- for (const QRect &rect : pixelRegion) {
- pointRegion += QRect(fromNative(rect.topLeft(), scaleFactor),
- fromNative(rect.size(), scaleFactor));
- }
- return pointRegion;
+ return scale(pixelRegion, qreal(1) / QHighDpiScaling::factor(window));
}
// When mapping expose events to Qt rects: round top/left towards the origin and
@@ -333,67 +239,7 @@ inline QRegion fromNativeLocalExposedRegion(const QRegion &pixelRegion, const QW
inline QRegion toNativeLocalRegion(const QRegion &pointRegion, const QWindow *window)
{
- if (!QHighDpiScaling::isActive())
- return pointRegion;
-
- qreal scaleFactor = QHighDpiScaling::factor(window);
- QRegion pixelRegon;
- for (const QRect &rect : pointRegion) {
- pixelRegon += QRect(toNative(rect.topLeft(), scaleFactor),
- toNative(rect.size(), scaleFactor));
- }
- return pixelRegon;
-}
-
-// Any T that has operator/()
-template <typename T, typename C>
-T fromNativePixels(const T &pixelValue, const C *context)
-{
- if (!QHighDpiScaling::isActive())
- return pixelValue;
-
- return pixelValue / QHighDpiScaling::factor(context);
-
-}
-
-// Any T that has operator*()
-template <typename T, typename C>
-T toNativePixels(const T &pointValue, const C *context)
-{
- if (!QHighDpiScaling::isActive())
- return pointValue;
-
- return pointValue * QHighDpiScaling::factor(context);
-}
-
-// Any QVector<T> where T has operator/()
-template <typename T>
-QVector<T> fromNativePixels(const QVector<T> &pixelValues, const QWindow *window)
-{
- if (!QHighDpiScaling::isActive())
- return pixelValues;
-
- QVector<T> pointValues;
- pointValues.reserve(pixelValues.size());
- const auto factor = QHighDpiScaling::factor(window);
- for (const T &pixelValue : pixelValues)
- pointValues.append(pixelValue / factor);
- return pointValues;
-}
-
-// Any QVector<T> where T has operator*()
-template <typename T>
-QVector<T> toNativePixels(const QVector<T> &pointValues, const QWindow *window)
-{
- if (!QHighDpiScaling::isActive())
- return pointValues;
-
- QVector<T> pixelValues;
- pixelValues.reserve(pointValues.size());
- const auto factor = QHighDpiScaling::factor(window);
- for (const T &pointValue : pointValues)
- pixelValues.append(pointValue * factor);
- return pixelValues;
+ return scale(pointRegion, QHighDpiScaling::factor(window));
}
} // namespace QHighDpi
diff --git a/src/gui/kernel/qkeysequence.cpp b/src/gui/kernel/qkeysequence.cpp
index 8688bb8403..2a86b340af 100644
--- a/src/gui/kernel/qkeysequence.cpp
+++ b/src/gui/kernel/qkeysequence.cpp
@@ -1518,7 +1518,7 @@ bool QKeySequence::operator< (const QKeySequence &other) const
*/
bool QKeySequence::isDetached() const
{
- return d->ref.load() == 1;
+ return d->ref.loadRelaxed() == 1;
}
/*!
diff --git a/src/gui/kernel/qopenglcontext.cpp b/src/gui/kernel/qopenglcontext.cpp
index 5bc6e27eb2..a3b2ea5f86 100644
--- a/src/gui/kernel/qopenglcontext.cpp
+++ b/src/gui/kernel/qopenglcontext.cpp
@@ -1634,7 +1634,7 @@ QOpenGLMultiGroupSharedResource::~QOpenGLMultiGroupSharedResource()
active.deref();
}
#ifndef QT_NO_DEBUG
- if (active.load() != 0) {
+ if (active.loadRelaxed() != 0) {
qWarning("QtGui: Resources are still available at program shutdown.\n"
" This is possibly caused by a leaked QOpenGLWidget, \n"
" QOpenGLFramebufferObject or QOpenGLPixelBuffer.");
diff --git a/src/gui/kernel/qopenglwindow.cpp b/src/gui/kernel/qopenglwindow.cpp
index 8b052d92ae..022a47c919 100644
--- a/src/gui/kernel/qopenglwindow.cpp
+++ b/src/gui/kernel/qopenglwindow.cpp
@@ -440,7 +440,7 @@ void QOpenGLWindow::makeCurrent()
d->context->makeCurrent(this);
} else {
if (!d->offscreenSurface) {
- d->offscreenSurface.reset(new QOffscreenSurface);
+ d->offscreenSurface.reset(new QOffscreenSurface(screen()));
d->offscreenSurface->setFormat(d->context->format());
d->offscreenSurface->create();
}
diff --git a/src/gui/kernel/qpalette.cpp b/src/gui/kernel/qpalette.cpp
index f6e5fa0a52..61dccd77ac 100644
--- a/src/gui/kernel/qpalette.cpp
+++ b/src/gui/kernel/qpalette.cpp
@@ -830,7 +830,7 @@ bool QPalette::isBrushSet(ColorGroup cg, ColorRole cr) const
*/
void QPalette::detach()
{
- if (d->ref.load() != 1) {
+ if (d->ref.loadRelaxed() != 1) {
QPalettePrivate *x = new QPalettePrivate;
for(int grp = 0; grp < (int)NColorGroups; grp++) {
for(int role = 0; role < (int)NColorRoles; role++)
@@ -1008,6 +1008,8 @@ QDataStream &operator<<(QDataStream &s, const QPalette &p)
max = QPalette::HighlightedText + 1;
else if (s.version() <= QDataStream::Qt_4_3)
max = QPalette::AlternateBase + 1;
+ else if (s.version() <= QDataStream::Qt_5_11)
+ max = QPalette::ToolTipText + 1;
for (int r = 0; r < max; r++)
s << p.d->br[grp][r];
}
@@ -1048,6 +1050,9 @@ QDataStream &operator>>(QDataStream &s, QPalette &p)
} else if (s.version() <= QDataStream::Qt_4_3) {
p = QPalette();
max = QPalette::AlternateBase + 1;
+ } else if (s.version() <= QDataStream::Qt_5_11) {
+ p = QPalette();
+ max = QPalette::ToolTipText + 1;
}
QBrush tmp;
diff --git a/src/gui/kernel/qsurfaceformat.cpp b/src/gui/kernel/qsurfaceformat.cpp
index 1a814ec21f..2e2738ec81 100644
--- a/src/gui/kernel/qsurfaceformat.cpp
+++ b/src/gui/kernel/qsurfaceformat.cpp
@@ -246,7 +246,7 @@ QSurfaceFormat::QSurfaceFormat(QSurfaceFormat::FormatOptions options) :
*/
void QSurfaceFormat::detach()
{
- if (d->ref.load() != 1) {
+ if (d->ref.loadRelaxed() != 1) {
QSurfaceFormatPrivate *newd = new QSurfaceFormatPrivate(d);
if (!d->ref.deref())
delete d;
diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp
index aad5f856be..b71a0c54aa 100644
--- a/src/gui/kernel/qwindow.cpp
+++ b/src/gui/kernel/qwindow.cpp
@@ -218,6 +218,12 @@ QWindow::~QWindow()
QGuiApplicationPrivate::window_list.removeAll(this);
if (!QGuiApplicationPrivate::is_app_closing)
QGuiApplicationPrivate::instance()->modalWindowList.removeOne(this);
+
+ // focus_window is normally cleared in destroy(), but the window may in
+ // some cases end up becoming the focus window again. Clear it again
+ // here as a workaround. See QTBUG-75326.
+ if (QGuiApplicationPrivate::focus_window == this)
+ QGuiApplicationPrivate::focus_window = 0;
}
void QWindowPrivate::init(QScreen *targetScreen)
diff --git a/src/gui/kernel/qwindowsysteminterface.cpp b/src/gui/kernel/qwindowsysteminterface.cpp
index 6f3edb10b4..4b8cb3646a 100644
--- a/src/gui/kernel/qwindowsysteminterface.cpp
+++ b/src/gui/kernel/qwindowsysteminterface.cpp
@@ -1115,7 +1115,7 @@ bool QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ProcessEventsFl
} else {
sendWindowSystemEvents(flags);
}
- return QWindowSystemInterfacePrivate::eventAccepted.load() > 0;
+ return QWindowSystemInterfacePrivate::eventAccepted.loadRelaxed() > 0;
}
void QWindowSystemInterface::deferredFlushWindowSystemEvents(QEventLoop::ProcessEventsFlags flags)
@@ -1156,7 +1156,7 @@ bool QWindowSystemInterface::sendWindowSystemEvents(QEventLoop::ProcessEventsFla
// (excluding flush events). This state can then be
// returned by flushWindowSystemEvents().
if (event->type != QWindowSystemInterfacePrivate::FlushEvents)
- QWindowSystemInterfacePrivate::eventAccepted.store(event->eventAccepted);
+ QWindowSystemInterfacePrivate::eventAccepted.storeRelaxed(event->eventAccepted);
delete event;
}
diff --git a/src/gui/math3d/qvector3d.cpp b/src/gui/math3d/qvector3d.cpp
index 12a7902272..4f72c1da66 100644
--- a/src/gui/math3d/qvector3d.cpp
+++ b/src/gui/math3d/qvector3d.cpp
@@ -514,7 +514,7 @@ float QVector3D::distanceToPlane
/*!
\overload
- Returns the distance from this vertex a plane defined by
+ Returns the distance from this vertex to a plane defined by
the vertices \a plane1, \a plane2 and \a plane3.
The return value will be negative if the vertex is below the plane,
diff --git a/src/gui/opengl/qopenglframebufferobject.cpp b/src/gui/opengl/qopenglframebufferobject.cpp
index e7631b09ce..5d30891565 100644
--- a/src/gui/opengl/qopenglframebufferobject.cpp
+++ b/src/gui/opengl/qopenglframebufferobject.cpp
@@ -183,7 +183,7 @@ QT_BEGIN_NAMESPACE
*/
void QOpenGLFramebufferObjectFormat::detach()
{
- if (d->ref.load() != 1) {
+ if (d->ref.loadRelaxed() != 1) {
QOpenGLFramebufferObjectFormatPrivate *newd
= new QOpenGLFramebufferObjectFormatPrivate(d);
if (!d->ref.deref())
diff --git a/src/gui/opengl/qopenglfunctions_1_0.cpp b/src/gui/opengl/qopenglfunctions_1_0.cpp
index 4235c9a339..f017c68fd9 100644
--- a/src/gui/opengl/qopenglfunctions_1_0.cpp
+++ b/src/gui/opengl/qopenglfunctions_1_0.cpp
@@ -76,11 +76,11 @@ QOpenGLFunctions_1_0::~QOpenGLFunctions_1_0()
{
if (d_1_0_Core) {
d_1_0_Core->refs.deref();
- Q_ASSERT(d_1_0_Core->refs.load());
+ Q_ASSERT(d_1_0_Core->refs.loadRelaxed());
}
if (d_1_0_Deprecated) {
d_1_0_Deprecated->refs.deref();
- Q_ASSERT(d_1_0_Deprecated->refs.load());
+ Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed());
}
}
diff --git a/src/gui/opengl/qopenglfunctions_1_1.cpp b/src/gui/opengl/qopenglfunctions_1_1.cpp
index 7d09bb40c1..a819d499f8 100644
--- a/src/gui/opengl/qopenglfunctions_1_1.cpp
+++ b/src/gui/opengl/qopenglfunctions_1_1.cpp
@@ -78,19 +78,19 @@ QOpenGLFunctions_1_1::~QOpenGLFunctions_1_1()
{
if (d_1_0_Core) {
d_1_0_Core->refs.deref();
- Q_ASSERT(d_1_0_Core->refs.load());
+ Q_ASSERT(d_1_0_Core->refs.loadRelaxed());
}
if (d_1_1_Core) {
d_1_1_Core->refs.deref();
- Q_ASSERT(d_1_1_Core->refs.load());
+ Q_ASSERT(d_1_1_Core->refs.loadRelaxed());
}
if (d_1_0_Deprecated) {
d_1_0_Deprecated->refs.deref();
- Q_ASSERT(d_1_0_Deprecated->refs.load());
+ Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed());
}
if (d_1_1_Deprecated) {
d_1_1_Deprecated->refs.deref();
- Q_ASSERT(d_1_1_Deprecated->refs.load());
+ Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed());
}
}
diff --git a/src/gui/opengl/qopenglfunctions_1_2.cpp b/src/gui/opengl/qopenglfunctions_1_2.cpp
index 94a9d64660..61db2b4e0f 100644
--- a/src/gui/opengl/qopenglfunctions_1_2.cpp
+++ b/src/gui/opengl/qopenglfunctions_1_2.cpp
@@ -80,27 +80,27 @@ QOpenGLFunctions_1_2::~QOpenGLFunctions_1_2()
{
if (d_1_0_Core) {
d_1_0_Core->refs.deref();
- Q_ASSERT(d_1_0_Core->refs.load());
+ Q_ASSERT(d_1_0_Core->refs.loadRelaxed());
}
if (d_1_1_Core) {
d_1_1_Core->refs.deref();
- Q_ASSERT(d_1_1_Core->refs.load());
+ Q_ASSERT(d_1_1_Core->refs.loadRelaxed());
}
if (d_1_2_Core) {
d_1_2_Core->refs.deref();
- Q_ASSERT(d_1_2_Core->refs.load());
+ Q_ASSERT(d_1_2_Core->refs.loadRelaxed());
}
if (d_1_0_Deprecated) {
d_1_0_Deprecated->refs.deref();
- Q_ASSERT(d_1_0_Deprecated->refs.load());
+ Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed());
}
if (d_1_1_Deprecated) {
d_1_1_Deprecated->refs.deref();
- Q_ASSERT(d_1_1_Deprecated->refs.load());
+ Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed());
}
if (d_1_2_Deprecated) {
d_1_2_Deprecated->refs.deref();
- Q_ASSERT(d_1_2_Deprecated->refs.load());
+ Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed());
}
}
diff --git a/src/gui/opengl/qopenglfunctions_1_3.cpp b/src/gui/opengl/qopenglfunctions_1_3.cpp
index 972ef9ff70..acc223ea74 100644
--- a/src/gui/opengl/qopenglfunctions_1_3.cpp
+++ b/src/gui/opengl/qopenglfunctions_1_3.cpp
@@ -82,35 +82,35 @@ QOpenGLFunctions_1_3::~QOpenGLFunctions_1_3()
{
if (d_1_0_Core) {
d_1_0_Core->refs.deref();
- Q_ASSERT(d_1_0_Core->refs.load());
+ Q_ASSERT(d_1_0_Core->refs.loadRelaxed());
}
if (d_1_1_Core) {
d_1_1_Core->refs.deref();
- Q_ASSERT(d_1_1_Core->refs.load());
+ Q_ASSERT(d_1_1_Core->refs.loadRelaxed());
}
if (d_1_2_Core) {
d_1_2_Core->refs.deref();
- Q_ASSERT(d_1_2_Core->refs.load());
+ Q_ASSERT(d_1_2_Core->refs.loadRelaxed());
}
if (d_1_3_Core) {
d_1_3_Core->refs.deref();
- Q_ASSERT(d_1_3_Core->refs.load());
+ Q_ASSERT(d_1_3_Core->refs.loadRelaxed());
}
if (d_1_0_Deprecated) {
d_1_0_Deprecated->refs.deref();
- Q_ASSERT(d_1_0_Deprecated->refs.load());
+ Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed());
}
if (d_1_1_Deprecated) {
d_1_1_Deprecated->refs.deref();
- Q_ASSERT(d_1_1_Deprecated->refs.load());
+ Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed());
}
if (d_1_2_Deprecated) {
d_1_2_Deprecated->refs.deref();
- Q_ASSERT(d_1_2_Deprecated->refs.load());
+ Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed());
}
if (d_1_3_Deprecated) {
d_1_3_Deprecated->refs.deref();
- Q_ASSERT(d_1_3_Deprecated->refs.load());
+ Q_ASSERT(d_1_3_Deprecated->refs.loadRelaxed());
}
}
diff --git a/src/gui/opengl/qopenglfunctions_1_4.cpp b/src/gui/opengl/qopenglfunctions_1_4.cpp
index 4b78253301..8e2349dc08 100644
--- a/src/gui/opengl/qopenglfunctions_1_4.cpp
+++ b/src/gui/opengl/qopenglfunctions_1_4.cpp
@@ -84,43 +84,43 @@ QOpenGLFunctions_1_4::~QOpenGLFunctions_1_4()
{
if (d_1_0_Core) {
d_1_0_Core->refs.deref();
- Q_ASSERT(d_1_0_Core->refs.load());
+ Q_ASSERT(d_1_0_Core->refs.loadRelaxed());
}
if (d_1_1_Core) {
d_1_1_Core->refs.deref();
- Q_ASSERT(d_1_1_Core->refs.load());
+ Q_ASSERT(d_1_1_Core->refs.loadRelaxed());
}
if (d_1_2_Core) {
d_1_2_Core->refs.deref();
- Q_ASSERT(d_1_2_Core->refs.load());
+ Q_ASSERT(d_1_2_Core->refs.loadRelaxed());
}
if (d_1_3_Core) {
d_1_3_Core->refs.deref();
- Q_ASSERT(d_1_3_Core->refs.load());
+ Q_ASSERT(d_1_3_Core->refs.loadRelaxed());
}
if (d_1_4_Core) {
d_1_4_Core->refs.deref();
- Q_ASSERT(d_1_4_Core->refs.load());
+ Q_ASSERT(d_1_4_Core->refs.loadRelaxed());
}
if (d_1_0_Deprecated) {
d_1_0_Deprecated->refs.deref();
- Q_ASSERT(d_1_0_Deprecated->refs.load());
+ Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed());
}
if (d_1_1_Deprecated) {
d_1_1_Deprecated->refs.deref();
- Q_ASSERT(d_1_1_Deprecated->refs.load());
+ Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed());
}
if (d_1_2_Deprecated) {
d_1_2_Deprecated->refs.deref();
- Q_ASSERT(d_1_2_Deprecated->refs.load());
+ Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed());
}
if (d_1_3_Deprecated) {
d_1_3_Deprecated->refs.deref();
- Q_ASSERT(d_1_3_Deprecated->refs.load());
+ Q_ASSERT(d_1_3_Deprecated->refs.loadRelaxed());
}
if (d_1_4_Deprecated) {
d_1_4_Deprecated->refs.deref();
- Q_ASSERT(d_1_4_Deprecated->refs.load());
+ Q_ASSERT(d_1_4_Deprecated->refs.loadRelaxed());
}
}
diff --git a/src/gui/opengl/qopenglfunctions_1_5.cpp b/src/gui/opengl/qopenglfunctions_1_5.cpp
index 2a0820d0cb..cd81cf8b35 100644
--- a/src/gui/opengl/qopenglfunctions_1_5.cpp
+++ b/src/gui/opengl/qopenglfunctions_1_5.cpp
@@ -85,47 +85,47 @@ QOpenGLFunctions_1_5::~QOpenGLFunctions_1_5()
{
if (d_1_0_Core) {
d_1_0_Core->refs.deref();
- Q_ASSERT(d_1_0_Core->refs.load());
+ Q_ASSERT(d_1_0_Core->refs.loadRelaxed());
}
if (d_1_1_Core) {
d_1_1_Core->refs.deref();
- Q_ASSERT(d_1_1_Core->refs.load());
+ Q_ASSERT(d_1_1_Core->refs.loadRelaxed());
}
if (d_1_2_Core) {
d_1_2_Core->refs.deref();
- Q_ASSERT(d_1_2_Core->refs.load());
+ Q_ASSERT(d_1_2_Core->refs.loadRelaxed());
}
if (d_1_3_Core) {
d_1_3_Core->refs.deref();
- Q_ASSERT(d_1_3_Core->refs.load());
+ Q_ASSERT(d_1_3_Core->refs.loadRelaxed());
}
if (d_1_4_Core) {
d_1_4_Core->refs.deref();
- Q_ASSERT(d_1_4_Core->refs.load());
+ Q_ASSERT(d_1_4_Core->refs.loadRelaxed());
}
if (d_1_5_Core) {
d_1_5_Core->refs.deref();
- Q_ASSERT(d_1_5_Core->refs.load());
+ Q_ASSERT(d_1_5_Core->refs.loadRelaxed());
}
if (d_1_0_Deprecated) {
d_1_0_Deprecated->refs.deref();
- Q_ASSERT(d_1_0_Deprecated->refs.load());
+ Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed());
}
if (d_1_1_Deprecated) {
d_1_1_Deprecated->refs.deref();
- Q_ASSERT(d_1_1_Deprecated->refs.load());
+ Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed());
}
if (d_1_2_Deprecated) {
d_1_2_Deprecated->refs.deref();
- Q_ASSERT(d_1_2_Deprecated->refs.load());
+ Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed());
}
if (d_1_3_Deprecated) {
d_1_3_Deprecated->refs.deref();
- Q_ASSERT(d_1_3_Deprecated->refs.load());
+ Q_ASSERT(d_1_3_Deprecated->refs.loadRelaxed());
}
if (d_1_4_Deprecated) {
d_1_4_Deprecated->refs.deref();
- Q_ASSERT(d_1_4_Deprecated->refs.load());
+ Q_ASSERT(d_1_4_Deprecated->refs.loadRelaxed());
}
}
diff --git a/src/gui/opengl/qopenglfunctions_2_0.cpp b/src/gui/opengl/qopenglfunctions_2_0.cpp
index 212723aa00..97a8c72fa6 100644
--- a/src/gui/opengl/qopenglfunctions_2_0.cpp
+++ b/src/gui/opengl/qopenglfunctions_2_0.cpp
@@ -87,51 +87,51 @@ QOpenGLFunctions_2_0::~QOpenGLFunctions_2_0()
{
if (d_1_0_Core) {
d_1_0_Core->refs.deref();
- Q_ASSERT(d_1_0_Core->refs.load());
+ Q_ASSERT(d_1_0_Core->refs.loadRelaxed());
}
if (d_1_1_Core) {
d_1_1_Core->refs.deref();
- Q_ASSERT(d_1_1_Core->refs.load());
+ Q_ASSERT(d_1_1_Core->refs.loadRelaxed());
}
if (d_1_2_Core) {
d_1_2_Core->refs.deref();
- Q_ASSERT(d_1_2_Core->refs.load());
+ Q_ASSERT(d_1_2_Core->refs.loadRelaxed());
}
if (d_1_3_Core) {
d_1_3_Core->refs.deref();
- Q_ASSERT(d_1_3_Core->refs.load());
+ Q_ASSERT(d_1_3_Core->refs.loadRelaxed());
}
if (d_1_4_Core) {
d_1_4_Core->refs.deref();
- Q_ASSERT(d_1_4_Core->refs.load());
+ Q_ASSERT(d_1_4_Core->refs.loadRelaxed());
}
if (d_1_5_Core) {
d_1_5_Core->refs.deref();
- Q_ASSERT(d_1_5_Core->refs.load());
+ Q_ASSERT(d_1_5_Core->refs.loadRelaxed());
}
if (d_2_0_Core) {
d_2_0_Core->refs.deref();
- Q_ASSERT(d_2_0_Core->refs.load());
+ Q_ASSERT(d_2_0_Core->refs.loadRelaxed());
}
if (d_1_0_Deprecated) {
d_1_0_Deprecated->refs.deref();
- Q_ASSERT(d_1_0_Deprecated->refs.load());
+ Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed());
}
if (d_1_1_Deprecated) {
d_1_1_Deprecated->refs.deref();
- Q_ASSERT(d_1_1_Deprecated->refs.load());
+ Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed());
}
if (d_1_2_Deprecated) {
d_1_2_Deprecated->refs.deref();
- Q_ASSERT(d_1_2_Deprecated->refs.load());
+ Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed());
}
if (d_1_3_Deprecated) {
d_1_3_Deprecated->refs.deref();
- Q_ASSERT(d_1_3_Deprecated->refs.load());
+ Q_ASSERT(d_1_3_Deprecated->refs.loadRelaxed());
}
if (d_1_4_Deprecated) {
d_1_4_Deprecated->refs.deref();
- Q_ASSERT(d_1_4_Deprecated->refs.load());
+ Q_ASSERT(d_1_4_Deprecated->refs.loadRelaxed());
}
}
diff --git a/src/gui/opengl/qopenglfunctions_2_1.cpp b/src/gui/opengl/qopenglfunctions_2_1.cpp
index b8b255014c..00bdc1bbba 100644
--- a/src/gui/opengl/qopenglfunctions_2_1.cpp
+++ b/src/gui/opengl/qopenglfunctions_2_1.cpp
@@ -88,55 +88,55 @@ QOpenGLFunctions_2_1::~QOpenGLFunctions_2_1()
{
if (d_1_0_Core) {
d_1_0_Core->refs.deref();
- Q_ASSERT(d_1_0_Core->refs.load());
+ Q_ASSERT(d_1_0_Core->refs.loadRelaxed());
}
if (d_1_1_Core) {
d_1_1_Core->refs.deref();
- Q_ASSERT(d_1_1_Core->refs.load());
+ Q_ASSERT(d_1_1_Core->refs.loadRelaxed());
}
if (d_1_2_Core) {
d_1_2_Core->refs.deref();
- Q_ASSERT(d_1_2_Core->refs.load());
+ Q_ASSERT(d_1_2_Core->refs.loadRelaxed());
}
if (d_1_3_Core) {
d_1_3_Core->refs.deref();
- Q_ASSERT(d_1_3_Core->refs.load());
+ Q_ASSERT(d_1_3_Core->refs.loadRelaxed());
}
if (d_1_4_Core) {
d_1_4_Core->refs.deref();
- Q_ASSERT(d_1_4_Core->refs.load());
+ Q_ASSERT(d_1_4_Core->refs.loadRelaxed());
}
if (d_1_5_Core) {
d_1_5_Core->refs.deref();
- Q_ASSERT(d_1_5_Core->refs.load());
+ Q_ASSERT(d_1_5_Core->refs.loadRelaxed());
}
if (d_2_0_Core) {
d_2_0_Core->refs.deref();
- Q_ASSERT(d_2_0_Core->refs.load());
+ Q_ASSERT(d_2_0_Core->refs.loadRelaxed());
}
if (d_2_1_Core) {
d_2_1_Core->refs.deref();
- Q_ASSERT(d_2_1_Core->refs.load());
+ Q_ASSERT(d_2_1_Core->refs.loadRelaxed());
}
if (d_1_0_Deprecated) {
d_1_0_Deprecated->refs.deref();
- Q_ASSERT(d_1_0_Deprecated->refs.load());
+ Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed());
}
if (d_1_1_Deprecated) {
d_1_1_Deprecated->refs.deref();
- Q_ASSERT(d_1_1_Deprecated->refs.load());
+ Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed());
}
if (d_1_2_Deprecated) {
d_1_2_Deprecated->refs.deref();
- Q_ASSERT(d_1_2_Deprecated->refs.load());
+ Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed());
}
if (d_1_3_Deprecated) {
d_1_3_Deprecated->refs.deref();
- Q_ASSERT(d_1_3_Deprecated->refs.load());
+ Q_ASSERT(d_1_3_Deprecated->refs.loadRelaxed());
}
if (d_1_4_Deprecated) {
d_1_4_Deprecated->refs.deref();
- Q_ASSERT(d_1_4_Deprecated->refs.load());
+ Q_ASSERT(d_1_4_Deprecated->refs.loadRelaxed());
}
}
diff --git a/src/gui/opengl/qopenglfunctions_3_0.cpp b/src/gui/opengl/qopenglfunctions_3_0.cpp
index 4972c03b1e..2c239dba1f 100644
--- a/src/gui/opengl/qopenglfunctions_3_0.cpp
+++ b/src/gui/opengl/qopenglfunctions_3_0.cpp
@@ -90,59 +90,59 @@ QOpenGLFunctions_3_0::~QOpenGLFunctions_3_0()
{
if (d_1_0_Core) {
d_1_0_Core->refs.deref();
- Q_ASSERT(d_1_0_Core->refs.load());
+ Q_ASSERT(d_1_0_Core->refs.loadRelaxed());
}
if (d_1_1_Core) {
d_1_1_Core->refs.deref();
- Q_ASSERT(d_1_1_Core->refs.load());
+ Q_ASSERT(d_1_1_Core->refs.loadRelaxed());
}
if (d_1_2_Core) {
d_1_2_Core->refs.deref();
- Q_ASSERT(d_1_2_Core->refs.load());
+ Q_ASSERT(d_1_2_Core->refs.loadRelaxed());
}
if (d_1_3_Core) {
d_1_3_Core->refs.deref();
- Q_ASSERT(d_1_3_Core->refs.load());
+ Q_ASSERT(d_1_3_Core->refs.loadRelaxed());
}
if (d_1_4_Core) {
d_1_4_Core->refs.deref();
- Q_ASSERT(d_1_4_Core->refs.load());
+ Q_ASSERT(d_1_4_Core->refs.loadRelaxed());
}
if (d_1_5_Core) {
d_1_5_Core->refs.deref();
- Q_ASSERT(d_1_5_Core->refs.load());
+ Q_ASSERT(d_1_5_Core->refs.loadRelaxed());
}
if (d_2_0_Core) {
d_2_0_Core->refs.deref();
- Q_ASSERT(d_2_0_Core->refs.load());
+ Q_ASSERT(d_2_0_Core->refs.loadRelaxed());
}
if (d_2_1_Core) {
d_2_1_Core->refs.deref();
- Q_ASSERT(d_2_1_Core->refs.load());
+ Q_ASSERT(d_2_1_Core->refs.loadRelaxed());
}
if (d_3_0_Core) {
d_3_0_Core->refs.deref();
- Q_ASSERT(d_3_0_Core->refs.load());
+ Q_ASSERT(d_3_0_Core->refs.loadRelaxed());
}
if (d_1_0_Deprecated) {
d_1_0_Deprecated->refs.deref();
- Q_ASSERT(d_1_0_Deprecated->refs.load());
+ Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed());
}
if (d_1_1_Deprecated) {
d_1_1_Deprecated->refs.deref();
- Q_ASSERT(d_1_1_Deprecated->refs.load());
+ Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed());
}
if (d_1_2_Deprecated) {
d_1_2_Deprecated->refs.deref();
- Q_ASSERT(d_1_2_Deprecated->refs.load());
+ Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed());
}
if (d_1_3_Deprecated) {
d_1_3_Deprecated->refs.deref();
- Q_ASSERT(d_1_3_Deprecated->refs.load());
+ Q_ASSERT(d_1_3_Deprecated->refs.loadRelaxed());
}
if (d_1_4_Deprecated) {
d_1_4_Deprecated->refs.deref();
- Q_ASSERT(d_1_4_Deprecated->refs.load());
+ Q_ASSERT(d_1_4_Deprecated->refs.loadRelaxed());
}
}
diff --git a/src/gui/opengl/qopenglfunctions_3_1.cpp b/src/gui/opengl/qopenglfunctions_3_1.cpp
index 9328f5ca89..f62f555c8e 100644
--- a/src/gui/opengl/qopenglfunctions_3_1.cpp
+++ b/src/gui/opengl/qopenglfunctions_3_1.cpp
@@ -84,43 +84,43 @@ QOpenGLFunctions_3_1::~QOpenGLFunctions_3_1()
{
if (d_1_0_Core) {
d_1_0_Core->refs.deref();
- Q_ASSERT(d_1_0_Core->refs.load());
+ Q_ASSERT(d_1_0_Core->refs.loadRelaxed());
}
if (d_1_1_Core) {
d_1_1_Core->refs.deref();
- Q_ASSERT(d_1_1_Core->refs.load());
+ Q_ASSERT(d_1_1_Core->refs.loadRelaxed());
}
if (d_1_2_Core) {
d_1_2_Core->refs.deref();
- Q_ASSERT(d_1_2_Core->refs.load());
+ Q_ASSERT(d_1_2_Core->refs.loadRelaxed());
}
if (d_1_3_Core) {
d_1_3_Core->refs.deref();
- Q_ASSERT(d_1_3_Core->refs.load());
+ Q_ASSERT(d_1_3_Core->refs.loadRelaxed());
}
if (d_1_4_Core) {
d_1_4_Core->refs.deref();
- Q_ASSERT(d_1_4_Core->refs.load());
+ Q_ASSERT(d_1_4_Core->refs.loadRelaxed());
}
if (d_1_5_Core) {
d_1_5_Core->refs.deref();
- Q_ASSERT(d_1_5_Core->refs.load());
+ Q_ASSERT(d_1_5_Core->refs.loadRelaxed());
}
if (d_2_0_Core) {
d_2_0_Core->refs.deref();
- Q_ASSERT(d_2_0_Core->refs.load());
+ Q_ASSERT(d_2_0_Core->refs.loadRelaxed());
}
if (d_2_1_Core) {
d_2_1_Core->refs.deref();
- Q_ASSERT(d_2_1_Core->refs.load());
+ Q_ASSERT(d_2_1_Core->refs.loadRelaxed());
}
if (d_3_0_Core) {
d_3_0_Core->refs.deref();
- Q_ASSERT(d_3_0_Core->refs.load());
+ Q_ASSERT(d_3_0_Core->refs.loadRelaxed());
}
if (d_3_1_Core) {
d_3_1_Core->refs.deref();
- Q_ASSERT(d_3_1_Core->refs.load());
+ Q_ASSERT(d_3_1_Core->refs.loadRelaxed());
}
}
diff --git a/src/gui/opengl/qopenglfunctions_3_2_compatibility.cpp b/src/gui/opengl/qopenglfunctions_3_2_compatibility.cpp
index 709f65edf8..ba7be2d893 100644
--- a/src/gui/opengl/qopenglfunctions_3_2_compatibility.cpp
+++ b/src/gui/opengl/qopenglfunctions_3_2_compatibility.cpp
@@ -92,67 +92,67 @@ QOpenGLFunctions_3_2_Compatibility::~QOpenGLFunctions_3_2_Compatibility()
{
if (d_1_0_Core) {
d_1_0_Core->refs.deref();
- Q_ASSERT(d_1_0_Core->refs.load());
+ Q_ASSERT(d_1_0_Core->refs.loadRelaxed());
}
if (d_1_1_Core) {
d_1_1_Core->refs.deref();
- Q_ASSERT(d_1_1_Core->refs.load());
+ Q_ASSERT(d_1_1_Core->refs.loadRelaxed());
}
if (d_1_2_Core) {
d_1_2_Core->refs.deref();
- Q_ASSERT(d_1_2_Core->refs.load());
+ Q_ASSERT(d_1_2_Core->refs.loadRelaxed());
}
if (d_1_3_Core) {
d_1_3_Core->refs.deref();
- Q_ASSERT(d_1_3_Core->refs.load());
+ Q_ASSERT(d_1_3_Core->refs.loadRelaxed());
}
if (d_1_4_Core) {
d_1_4_Core->refs.deref();
- Q_ASSERT(d_1_4_Core->refs.load());
+ Q_ASSERT(d_1_4_Core->refs.loadRelaxed());
}
if (d_1_5_Core) {
d_1_5_Core->refs.deref();
- Q_ASSERT(d_1_5_Core->refs.load());
+ Q_ASSERT(d_1_5_Core->refs.loadRelaxed());
}
if (d_2_0_Core) {
d_2_0_Core->refs.deref();
- Q_ASSERT(d_2_0_Core->refs.load());
+ Q_ASSERT(d_2_0_Core->refs.loadRelaxed());
}
if (d_2_1_Core) {
d_2_1_Core->refs.deref();
- Q_ASSERT(d_2_1_Core->refs.load());
+ Q_ASSERT(d_2_1_Core->refs.loadRelaxed());
}
if (d_3_0_Core) {
d_3_0_Core->refs.deref();
- Q_ASSERT(d_3_0_Core->refs.load());
+ Q_ASSERT(d_3_0_Core->refs.loadRelaxed());
}
if (d_3_1_Core) {
d_3_1_Core->refs.deref();
- Q_ASSERT(d_3_1_Core->refs.load());
+ Q_ASSERT(d_3_1_Core->refs.loadRelaxed());
}
if (d_3_2_Core) {
d_3_2_Core->refs.deref();
- Q_ASSERT(d_3_2_Core->refs.load());
+ Q_ASSERT(d_3_2_Core->refs.loadRelaxed());
}
if (d_1_0_Deprecated) {
d_1_0_Deprecated->refs.deref();
- Q_ASSERT(d_1_0_Deprecated->refs.load());
+ Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed());
}
if (d_1_1_Deprecated) {
d_1_1_Deprecated->refs.deref();
- Q_ASSERT(d_1_1_Deprecated->refs.load());
+ Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed());
}
if (d_1_2_Deprecated) {
d_1_2_Deprecated->refs.deref();
- Q_ASSERT(d_1_2_Deprecated->refs.load());
+ Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed());
}
if (d_1_3_Deprecated) {
d_1_3_Deprecated->refs.deref();
- Q_ASSERT(d_1_3_Deprecated->refs.load());
+ Q_ASSERT(d_1_3_Deprecated->refs.loadRelaxed());
}
if (d_1_4_Deprecated) {
d_1_4_Deprecated->refs.deref();
- Q_ASSERT(d_1_4_Deprecated->refs.load());
+ Q_ASSERT(d_1_4_Deprecated->refs.loadRelaxed());
}
}
diff --git a/src/gui/opengl/qopenglfunctions_3_2_core.cpp b/src/gui/opengl/qopenglfunctions_3_2_core.cpp
index 02c0c78b01..4c1e3eb3da 100644
--- a/src/gui/opengl/qopenglfunctions_3_2_core.cpp
+++ b/src/gui/opengl/qopenglfunctions_3_2_core.cpp
@@ -85,47 +85,47 @@ QOpenGLFunctions_3_2_Core::~QOpenGLFunctions_3_2_Core()
{
if (d_1_0_Core) {
d_1_0_Core->refs.deref();
- Q_ASSERT(d_1_0_Core->refs.load());
+ Q_ASSERT(d_1_0_Core->refs.loadRelaxed());
}
if (d_1_1_Core) {
d_1_1_Core->refs.deref();
- Q_ASSERT(d_1_1_Core->refs.load());
+ Q_ASSERT(d_1_1_Core->refs.loadRelaxed());
}
if (d_1_2_Core) {
d_1_2_Core->refs.deref();
- Q_ASSERT(d_1_2_Core->refs.load());
+ Q_ASSERT(d_1_2_Core->refs.loadRelaxed());
}
if (d_1_3_Core) {
d_1_3_Core->refs.deref();
- Q_ASSERT(d_1_3_Core->refs.load());
+ Q_ASSERT(d_1_3_Core->refs.loadRelaxed());
}
if (d_1_4_Core) {
d_1_4_Core->refs.deref();
- Q_ASSERT(d_1_4_Core->refs.load());
+ Q_ASSERT(d_1_4_Core->refs.loadRelaxed());
}
if (d_1_5_Core) {
d_1_5_Core->refs.deref();
- Q_ASSERT(d_1_5_Core->refs.load());
+ Q_ASSERT(d_1_5_Core->refs.loadRelaxed());
}
if (d_2_0_Core) {
d_2_0_Core->refs.deref();
- Q_ASSERT(d_2_0_Core->refs.load());
+ Q_ASSERT(d_2_0_Core->refs.loadRelaxed());
}
if (d_2_1_Core) {
d_2_1_Core->refs.deref();
- Q_ASSERT(d_2_1_Core->refs.load());
+ Q_ASSERT(d_2_1_Core->refs.loadRelaxed());
}
if (d_3_0_Core) {
d_3_0_Core->refs.deref();
- Q_ASSERT(d_3_0_Core->refs.load());
+ Q_ASSERT(d_3_0_Core->refs.loadRelaxed());
}
if (d_3_1_Core) {
d_3_1_Core->refs.deref();
- Q_ASSERT(d_3_1_Core->refs.load());
+ Q_ASSERT(d_3_1_Core->refs.loadRelaxed());
}
if (d_3_2_Core) {
d_3_2_Core->refs.deref();
- Q_ASSERT(d_3_2_Core->refs.load());
+ Q_ASSERT(d_3_2_Core->refs.loadRelaxed());
}
}
diff --git a/src/gui/opengl/qopenglfunctions_3_3_compatibility.cpp b/src/gui/opengl/qopenglfunctions_3_3_compatibility.cpp
index b034391c86..c750c6e0cc 100644
--- a/src/gui/opengl/qopenglfunctions_3_3_compatibility.cpp
+++ b/src/gui/opengl/qopenglfunctions_3_3_compatibility.cpp
@@ -93,75 +93,75 @@ QOpenGLFunctions_3_3_Compatibility::~QOpenGLFunctions_3_3_Compatibility()
{
if (d_1_0_Core) {
d_1_0_Core->refs.deref();
- Q_ASSERT(d_1_0_Core->refs.load());
+ Q_ASSERT(d_1_0_Core->refs.loadRelaxed());
}
if (d_1_1_Core) {
d_1_1_Core->refs.deref();
- Q_ASSERT(d_1_1_Core->refs.load());
+ Q_ASSERT(d_1_1_Core->refs.loadRelaxed());
}
if (d_1_2_Core) {
d_1_2_Core->refs.deref();
- Q_ASSERT(d_1_2_Core->refs.load());
+ Q_ASSERT(d_1_2_Core->refs.loadRelaxed());
}
if (d_1_3_Core) {
d_1_3_Core->refs.deref();
- Q_ASSERT(d_1_3_Core->refs.load());
+ Q_ASSERT(d_1_3_Core->refs.loadRelaxed());
}
if (d_1_4_Core) {
d_1_4_Core->refs.deref();
- Q_ASSERT(d_1_4_Core->refs.load());
+ Q_ASSERT(d_1_4_Core->refs.loadRelaxed());
}
if (d_1_5_Core) {
d_1_5_Core->refs.deref();
- Q_ASSERT(d_1_5_Core->refs.load());
+ Q_ASSERT(d_1_5_Core->refs.loadRelaxed());
}
if (d_2_0_Core) {
d_2_0_Core->refs.deref();
- Q_ASSERT(d_2_0_Core->refs.load());
+ Q_ASSERT(d_2_0_Core->refs.loadRelaxed());
}
if (d_2_1_Core) {
d_2_1_Core->refs.deref();
- Q_ASSERT(d_2_1_Core->refs.load());
+ Q_ASSERT(d_2_1_Core->refs.loadRelaxed());
}
if (d_3_0_Core) {
d_3_0_Core->refs.deref();
- Q_ASSERT(d_3_0_Core->refs.load());
+ Q_ASSERT(d_3_0_Core->refs.loadRelaxed());
}
if (d_3_1_Core) {
d_3_1_Core->refs.deref();
- Q_ASSERT(d_3_1_Core->refs.load());
+ Q_ASSERT(d_3_1_Core->refs.loadRelaxed());
}
if (d_3_2_Core) {
d_3_2_Core->refs.deref();
- Q_ASSERT(d_3_2_Core->refs.load());
+ Q_ASSERT(d_3_2_Core->refs.loadRelaxed());
}
if (d_3_3_Core) {
d_3_3_Core->refs.deref();
- Q_ASSERT(d_3_3_Core->refs.load());
+ Q_ASSERT(d_3_3_Core->refs.loadRelaxed());
}
if (d_1_0_Deprecated) {
d_1_0_Deprecated->refs.deref();
- Q_ASSERT(d_1_0_Deprecated->refs.load());
+ Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed());
}
if (d_1_1_Deprecated) {
d_1_1_Deprecated->refs.deref();
- Q_ASSERT(d_1_1_Deprecated->refs.load());
+ Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed());
}
if (d_1_2_Deprecated) {
d_1_2_Deprecated->refs.deref();
- Q_ASSERT(d_1_2_Deprecated->refs.load());
+ Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed());
}
if (d_1_3_Deprecated) {
d_1_3_Deprecated->refs.deref();
- Q_ASSERT(d_1_3_Deprecated->refs.load());
+ Q_ASSERT(d_1_3_Deprecated->refs.loadRelaxed());
}
if (d_1_4_Deprecated) {
d_1_4_Deprecated->refs.deref();
- Q_ASSERT(d_1_4_Deprecated->refs.load());
+ Q_ASSERT(d_1_4_Deprecated->refs.loadRelaxed());
}
if (d_3_3_Deprecated) {
d_3_3_Deprecated->refs.deref();
- Q_ASSERT(d_3_3_Deprecated->refs.load());
+ Q_ASSERT(d_3_3_Deprecated->refs.loadRelaxed());
}
}
diff --git a/src/gui/opengl/qopenglfunctions_3_3_core.cpp b/src/gui/opengl/qopenglfunctions_3_3_core.cpp
index 7779d92b6a..5723509e32 100644
--- a/src/gui/opengl/qopenglfunctions_3_3_core.cpp
+++ b/src/gui/opengl/qopenglfunctions_3_3_core.cpp
@@ -86,51 +86,51 @@ QOpenGLFunctions_3_3_Core::~QOpenGLFunctions_3_3_Core()
{
if (d_1_0_Core) {
d_1_0_Core->refs.deref();
- Q_ASSERT(d_1_0_Core->refs.load());
+ Q_ASSERT(d_1_0_Core->refs.loadRelaxed());
}
if (d_1_1_Core) {
d_1_1_Core->refs.deref();
- Q_ASSERT(d_1_1_Core->refs.load());
+ Q_ASSERT(d_1_1_Core->refs.loadRelaxed());
}
if (d_1_2_Core) {
d_1_2_Core->refs.deref();
- Q_ASSERT(d_1_2_Core->refs.load());
+ Q_ASSERT(d_1_2_Core->refs.loadRelaxed());
}
if (d_1_3_Core) {
d_1_3_Core->refs.deref();
- Q_ASSERT(d_1_3_Core->refs.load());
+ Q_ASSERT(d_1_3_Core->refs.loadRelaxed());
}
if (d_1_4_Core) {
d_1_4_Core->refs.deref();
- Q_ASSERT(d_1_4_Core->refs.load());
+ Q_ASSERT(d_1_4_Core->refs.loadRelaxed());
}
if (d_1_5_Core) {
d_1_5_Core->refs.deref();
- Q_ASSERT(d_1_5_Core->refs.load());
+ Q_ASSERT(d_1_5_Core->refs.loadRelaxed());
}
if (d_2_0_Core) {
d_2_0_Core->refs.deref();
- Q_ASSERT(d_2_0_Core->refs.load());
+ Q_ASSERT(d_2_0_Core->refs.loadRelaxed());
}
if (d_2_1_Core) {
d_2_1_Core->refs.deref();
- Q_ASSERT(d_2_1_Core->refs.load());
+ Q_ASSERT(d_2_1_Core->refs.loadRelaxed());
}
if (d_3_0_Core) {
d_3_0_Core->refs.deref();
- Q_ASSERT(d_3_0_Core->refs.load());
+ Q_ASSERT(d_3_0_Core->refs.loadRelaxed());
}
if (d_3_1_Core) {
d_3_1_Core->refs.deref();
- Q_ASSERT(d_3_1_Core->refs.load());
+ Q_ASSERT(d_3_1_Core->refs.loadRelaxed());
}
if (d_3_2_Core) {
d_3_2_Core->refs.deref();
- Q_ASSERT(d_3_2_Core->refs.load());
+ Q_ASSERT(d_3_2_Core->refs.loadRelaxed());
}
if (d_3_3_Core) {
d_3_3_Core->refs.deref();
- Q_ASSERT(d_3_3_Core->refs.load());
+ Q_ASSERT(d_3_3_Core->refs.loadRelaxed());
}
}
diff --git a/src/gui/opengl/qopenglfunctions_4_0_compatibility.cpp b/src/gui/opengl/qopenglfunctions_4_0_compatibility.cpp
index 4fe4526efc..6ae7643eb5 100644
--- a/src/gui/opengl/qopenglfunctions_4_0_compatibility.cpp
+++ b/src/gui/opengl/qopenglfunctions_4_0_compatibility.cpp
@@ -94,79 +94,79 @@ QOpenGLFunctions_4_0_Compatibility::~QOpenGLFunctions_4_0_Compatibility()
{
if (d_1_0_Core) {
d_1_0_Core->refs.deref();
- Q_ASSERT(d_1_0_Core->refs.load());
+ Q_ASSERT(d_1_0_Core->refs.loadRelaxed());
}
if (d_1_1_Core) {
d_1_1_Core->refs.deref();
- Q_ASSERT(d_1_1_Core->refs.load());
+ Q_ASSERT(d_1_1_Core->refs.loadRelaxed());
}
if (d_1_2_Core) {
d_1_2_Core->refs.deref();
- Q_ASSERT(d_1_2_Core->refs.load());
+ Q_ASSERT(d_1_2_Core->refs.loadRelaxed());
}
if (d_1_3_Core) {
d_1_3_Core->refs.deref();
- Q_ASSERT(d_1_3_Core->refs.load());
+ Q_ASSERT(d_1_3_Core->refs.loadRelaxed());
}
if (d_1_4_Core) {
d_1_4_Core->refs.deref();
- Q_ASSERT(d_1_4_Core->refs.load());
+ Q_ASSERT(d_1_4_Core->refs.loadRelaxed());
}
if (d_1_5_Core) {
d_1_5_Core->refs.deref();
- Q_ASSERT(d_1_5_Core->refs.load());
+ Q_ASSERT(d_1_5_Core->refs.loadRelaxed());
}
if (d_2_0_Core) {
d_2_0_Core->refs.deref();
- Q_ASSERT(d_2_0_Core->refs.load());
+ Q_ASSERT(d_2_0_Core->refs.loadRelaxed());
}
if (d_2_1_Core) {
d_2_1_Core->refs.deref();
- Q_ASSERT(d_2_1_Core->refs.load());
+ Q_ASSERT(d_2_1_Core->refs.loadRelaxed());
}
if (d_3_0_Core) {
d_3_0_Core->refs.deref();
- Q_ASSERT(d_3_0_Core->refs.load());
+ Q_ASSERT(d_3_0_Core->refs.loadRelaxed());
}
if (d_3_1_Core) {
d_3_1_Core->refs.deref();
- Q_ASSERT(d_3_1_Core->refs.load());
+ Q_ASSERT(d_3_1_Core->refs.loadRelaxed());
}
if (d_3_2_Core) {
d_3_2_Core->refs.deref();
- Q_ASSERT(d_3_2_Core->refs.load());
+ Q_ASSERT(d_3_2_Core->refs.loadRelaxed());
}
if (d_3_3_Core) {
d_3_3_Core->refs.deref();
- Q_ASSERT(d_3_3_Core->refs.load());
+ Q_ASSERT(d_3_3_Core->refs.loadRelaxed());
}
if (d_4_0_Core) {
d_4_0_Core->refs.deref();
- Q_ASSERT(d_4_0_Core->refs.load());
+ Q_ASSERT(d_4_0_Core->refs.loadRelaxed());
}
if (d_1_0_Deprecated) {
d_1_0_Deprecated->refs.deref();
- Q_ASSERT(d_1_0_Deprecated->refs.load());
+ Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed());
}
if (d_1_1_Deprecated) {
d_1_1_Deprecated->refs.deref();
- Q_ASSERT(d_1_1_Deprecated->refs.load());
+ Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed());
}
if (d_1_2_Deprecated) {
d_1_2_Deprecated->refs.deref();
- Q_ASSERT(d_1_2_Deprecated->refs.load());
+ Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed());
}
if (d_1_3_Deprecated) {
d_1_3_Deprecated->refs.deref();
- Q_ASSERT(d_1_3_Deprecated->refs.load());
+ Q_ASSERT(d_1_3_Deprecated->refs.loadRelaxed());
}
if (d_1_4_Deprecated) {
d_1_4_Deprecated->refs.deref();
- Q_ASSERT(d_1_4_Deprecated->refs.load());
+ Q_ASSERT(d_1_4_Deprecated->refs.loadRelaxed());
}
if (d_3_3_Deprecated) {
d_3_3_Deprecated->refs.deref();
- Q_ASSERT(d_3_3_Deprecated->refs.load());
+ Q_ASSERT(d_3_3_Deprecated->refs.loadRelaxed());
}
}
diff --git a/src/gui/opengl/qopenglfunctions_4_0_core.cpp b/src/gui/opengl/qopenglfunctions_4_0_core.cpp
index 4e4e8cc547..cd4fdb8b2b 100644
--- a/src/gui/opengl/qopenglfunctions_4_0_core.cpp
+++ b/src/gui/opengl/qopenglfunctions_4_0_core.cpp
@@ -87,55 +87,55 @@ QOpenGLFunctions_4_0_Core::~QOpenGLFunctions_4_0_Core()
{
if (d_1_0_Core) {
d_1_0_Core->refs.deref();
- Q_ASSERT(d_1_0_Core->refs.load());
+ Q_ASSERT(d_1_0_Core->refs.loadRelaxed());
}
if (d_1_1_Core) {
d_1_1_Core->refs.deref();
- Q_ASSERT(d_1_1_Core->refs.load());
+ Q_ASSERT(d_1_1_Core->refs.loadRelaxed());
}
if (d_1_2_Core) {
d_1_2_Core->refs.deref();
- Q_ASSERT(d_1_2_Core->refs.load());
+ Q_ASSERT(d_1_2_Core->refs.loadRelaxed());
}
if (d_1_3_Core) {
d_1_3_Core->refs.deref();
- Q_ASSERT(d_1_3_Core->refs.load());
+ Q_ASSERT(d_1_3_Core->refs.loadRelaxed());
}
if (d_1_4_Core) {
d_1_4_Core->refs.deref();
- Q_ASSERT(d_1_4_Core->refs.load());
+ Q_ASSERT(d_1_4_Core->refs.loadRelaxed());
}
if (d_1_5_Core) {
d_1_5_Core->refs.deref();
- Q_ASSERT(d_1_5_Core->refs.load());
+ Q_ASSERT(d_1_5_Core->refs.loadRelaxed());
}
if (d_2_0_Core) {
d_2_0_Core->refs.deref();
- Q_ASSERT(d_2_0_Core->refs.load());
+ Q_ASSERT(d_2_0_Core->refs.loadRelaxed());
}
if (d_2_1_Core) {
d_2_1_Core->refs.deref();
- Q_ASSERT(d_2_1_Core->refs.load());
+ Q_ASSERT(d_2_1_Core->refs.loadRelaxed());
}
if (d_3_0_Core) {
d_3_0_Core->refs.deref();
- Q_ASSERT(d_3_0_Core->refs.load());
+ Q_ASSERT(d_3_0_Core->refs.loadRelaxed());
}
if (d_3_1_Core) {
d_3_1_Core->refs.deref();
- Q_ASSERT(d_3_1_Core->refs.load());
+ Q_ASSERT(d_3_1_Core->refs.loadRelaxed());
}
if (d_3_2_Core) {
d_3_2_Core->refs.deref();
- Q_ASSERT(d_3_2_Core->refs.load());
+ Q_ASSERT(d_3_2_Core->refs.loadRelaxed());
}
if (d_3_3_Core) {
d_3_3_Core->refs.deref();
- Q_ASSERT(d_3_3_Core->refs.load());
+ Q_ASSERT(d_3_3_Core->refs.loadRelaxed());
}
if (d_4_0_Core) {
d_4_0_Core->refs.deref();
- Q_ASSERT(d_4_0_Core->refs.load());
+ Q_ASSERT(d_4_0_Core->refs.loadRelaxed());
}
}
diff --git a/src/gui/opengl/qopenglfunctions_4_1_compatibility.cpp b/src/gui/opengl/qopenglfunctions_4_1_compatibility.cpp
index 41ecb4672a..d104c74bc2 100644
--- a/src/gui/opengl/qopenglfunctions_4_1_compatibility.cpp
+++ b/src/gui/opengl/qopenglfunctions_4_1_compatibility.cpp
@@ -95,83 +95,83 @@ QOpenGLFunctions_4_1_Compatibility::~QOpenGLFunctions_4_1_Compatibility()
{
if (d_1_0_Core) {
d_1_0_Core->refs.deref();
- Q_ASSERT(d_1_0_Core->refs.load());
+ Q_ASSERT(d_1_0_Core->refs.loadRelaxed());
}
if (d_1_1_Core) {
d_1_1_Core->refs.deref();
- Q_ASSERT(d_1_1_Core->refs.load());
+ Q_ASSERT(d_1_1_Core->refs.loadRelaxed());
}
if (d_1_2_Core) {
d_1_2_Core->refs.deref();
- Q_ASSERT(d_1_2_Core->refs.load());
+ Q_ASSERT(d_1_2_Core->refs.loadRelaxed());
}
if (d_1_3_Core) {
d_1_3_Core->refs.deref();
- Q_ASSERT(d_1_3_Core->refs.load());
+ Q_ASSERT(d_1_3_Core->refs.loadRelaxed());
}
if (d_1_4_Core) {
d_1_4_Core->refs.deref();
- Q_ASSERT(d_1_4_Core->refs.load());
+ Q_ASSERT(d_1_4_Core->refs.loadRelaxed());
}
if (d_1_5_Core) {
d_1_5_Core->refs.deref();
- Q_ASSERT(d_1_5_Core->refs.load());
+ Q_ASSERT(d_1_5_Core->refs.loadRelaxed());
}
if (d_2_0_Core) {
d_2_0_Core->refs.deref();
- Q_ASSERT(d_2_0_Core->refs.load());
+ Q_ASSERT(d_2_0_Core->refs.loadRelaxed());
}
if (d_2_1_Core) {
d_2_1_Core->refs.deref();
- Q_ASSERT(d_2_1_Core->refs.load());
+ Q_ASSERT(d_2_1_Core->refs.loadRelaxed());
}
if (d_3_0_Core) {
d_3_0_Core->refs.deref();
- Q_ASSERT(d_3_0_Core->refs.load());
+ Q_ASSERT(d_3_0_Core->refs.loadRelaxed());
}
if (d_3_1_Core) {
d_3_1_Core->refs.deref();
- Q_ASSERT(d_3_1_Core->refs.load());
+ Q_ASSERT(d_3_1_Core->refs.loadRelaxed());
}
if (d_3_2_Core) {
d_3_2_Core->refs.deref();
- Q_ASSERT(d_3_2_Core->refs.load());
+ Q_ASSERT(d_3_2_Core->refs.loadRelaxed());
}
if (d_3_3_Core) {
d_3_3_Core->refs.deref();
- Q_ASSERT(d_3_3_Core->refs.load());
+ Q_ASSERT(d_3_3_Core->refs.loadRelaxed());
}
if (d_4_0_Core) {
d_4_0_Core->refs.deref();
- Q_ASSERT(d_4_0_Core->refs.load());
+ Q_ASSERT(d_4_0_Core->refs.loadRelaxed());
}
if (d_4_1_Core) {
d_4_1_Core->refs.deref();
- Q_ASSERT(d_4_1_Core->refs.load());
+ Q_ASSERT(d_4_1_Core->refs.loadRelaxed());
}
if (d_1_0_Deprecated) {
d_1_0_Deprecated->refs.deref();
- Q_ASSERT(d_1_0_Deprecated->refs.load());
+ Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed());
}
if (d_1_1_Deprecated) {
d_1_1_Deprecated->refs.deref();
- Q_ASSERT(d_1_1_Deprecated->refs.load());
+ Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed());
}
if (d_1_2_Deprecated) {
d_1_2_Deprecated->refs.deref();
- Q_ASSERT(d_1_2_Deprecated->refs.load());
+ Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed());
}
if (d_1_3_Deprecated) {
d_1_3_Deprecated->refs.deref();
- Q_ASSERT(d_1_3_Deprecated->refs.load());
+ Q_ASSERT(d_1_3_Deprecated->refs.loadRelaxed());
}
if (d_1_4_Deprecated) {
d_1_4_Deprecated->refs.deref();
- Q_ASSERT(d_1_4_Deprecated->refs.load());
+ Q_ASSERT(d_1_4_Deprecated->refs.loadRelaxed());
}
if (d_3_3_Deprecated) {
d_3_3_Deprecated->refs.deref();
- Q_ASSERT(d_3_3_Deprecated->refs.load());
+ Q_ASSERT(d_3_3_Deprecated->refs.loadRelaxed());
}
}
diff --git a/src/gui/opengl/qopenglfunctions_4_1_core.cpp b/src/gui/opengl/qopenglfunctions_4_1_core.cpp
index 5a1e1eb42f..7527aba620 100644
--- a/src/gui/opengl/qopenglfunctions_4_1_core.cpp
+++ b/src/gui/opengl/qopenglfunctions_4_1_core.cpp
@@ -88,59 +88,59 @@ QOpenGLFunctions_4_1_Core::~QOpenGLFunctions_4_1_Core()
{
if (d_1_0_Core) {
d_1_0_Core->refs.deref();
- Q_ASSERT(d_1_0_Core->refs.load());
+ Q_ASSERT(d_1_0_Core->refs.loadRelaxed());
}
if (d_1_1_Core) {
d_1_1_Core->refs.deref();
- Q_ASSERT(d_1_1_Core->refs.load());
+ Q_ASSERT(d_1_1_Core->refs.loadRelaxed());
}
if (d_1_2_Core) {
d_1_2_Core->refs.deref();
- Q_ASSERT(d_1_2_Core->refs.load());
+ Q_ASSERT(d_1_2_Core->refs.loadRelaxed());
}
if (d_1_3_Core) {
d_1_3_Core->refs.deref();
- Q_ASSERT(d_1_3_Core->refs.load());
+ Q_ASSERT(d_1_3_Core->refs.loadRelaxed());
}
if (d_1_4_Core) {
d_1_4_Core->refs.deref();
- Q_ASSERT(d_1_4_Core->refs.load());
+ Q_ASSERT(d_1_4_Core->refs.loadRelaxed());
}
if (d_1_5_Core) {
d_1_5_Core->refs.deref();
- Q_ASSERT(d_1_5_Core->refs.load());
+ Q_ASSERT(d_1_5_Core->refs.loadRelaxed());
}
if (d_2_0_Core) {
d_2_0_Core->refs.deref();
- Q_ASSERT(d_2_0_Core->refs.load());
+ Q_ASSERT(d_2_0_Core->refs.loadRelaxed());
}
if (d_2_1_Core) {
d_2_1_Core->refs.deref();
- Q_ASSERT(d_2_1_Core->refs.load());
+ Q_ASSERT(d_2_1_Core->refs.loadRelaxed());
}
if (d_3_0_Core) {
d_3_0_Core->refs.deref();
- Q_ASSERT(d_3_0_Core->refs.load());
+ Q_ASSERT(d_3_0_Core->refs.loadRelaxed());
}
if (d_3_1_Core) {
d_3_1_Core->refs.deref();
- Q_ASSERT(d_3_1_Core->refs.load());
+ Q_ASSERT(d_3_1_Core->refs.loadRelaxed());
}
if (d_3_2_Core) {
d_3_2_Core->refs.deref();
- Q_ASSERT(d_3_2_Core->refs.load());
+ Q_ASSERT(d_3_2_Core->refs.loadRelaxed());
}
if (d_3_3_Core) {
d_3_3_Core->refs.deref();
- Q_ASSERT(d_3_3_Core->refs.load());
+ Q_ASSERT(d_3_3_Core->refs.loadRelaxed());
}
if (d_4_0_Core) {
d_4_0_Core->refs.deref();
- Q_ASSERT(d_4_0_Core->refs.load());
+ Q_ASSERT(d_4_0_Core->refs.loadRelaxed());
}
if (d_4_1_Core) {
d_4_1_Core->refs.deref();
- Q_ASSERT(d_4_1_Core->refs.load());
+ Q_ASSERT(d_4_1_Core->refs.loadRelaxed());
}
}
diff --git a/src/gui/opengl/qopenglfunctions_4_2_compatibility.cpp b/src/gui/opengl/qopenglfunctions_4_2_compatibility.cpp
index fcc049c67b..a5b1b37495 100644
--- a/src/gui/opengl/qopenglfunctions_4_2_compatibility.cpp
+++ b/src/gui/opengl/qopenglfunctions_4_2_compatibility.cpp
@@ -96,87 +96,87 @@ QOpenGLFunctions_4_2_Compatibility::~QOpenGLFunctions_4_2_Compatibility()
{
if (d_1_0_Core) {
d_1_0_Core->refs.deref();
- Q_ASSERT(d_1_0_Core->refs.load());
+ Q_ASSERT(d_1_0_Core->refs.loadRelaxed());
}
if (d_1_1_Core) {
d_1_1_Core->refs.deref();
- Q_ASSERT(d_1_1_Core->refs.load());
+ Q_ASSERT(d_1_1_Core->refs.loadRelaxed());
}
if (d_1_2_Core) {
d_1_2_Core->refs.deref();
- Q_ASSERT(d_1_2_Core->refs.load());
+ Q_ASSERT(d_1_2_Core->refs.loadRelaxed());
}
if (d_1_3_Core) {
d_1_3_Core->refs.deref();
- Q_ASSERT(d_1_3_Core->refs.load());
+ Q_ASSERT(d_1_3_Core->refs.loadRelaxed());
}
if (d_1_4_Core) {
d_1_4_Core->refs.deref();
- Q_ASSERT(d_1_4_Core->refs.load());
+ Q_ASSERT(d_1_4_Core->refs.loadRelaxed());
}
if (d_1_5_Core) {
d_1_5_Core->refs.deref();
- Q_ASSERT(d_1_5_Core->refs.load());
+ Q_ASSERT(d_1_5_Core->refs.loadRelaxed());
}
if (d_2_0_Core) {
d_2_0_Core->refs.deref();
- Q_ASSERT(d_2_0_Core->refs.load());
+ Q_ASSERT(d_2_0_Core->refs.loadRelaxed());
}
if (d_2_1_Core) {
d_2_1_Core->refs.deref();
- Q_ASSERT(d_2_1_Core->refs.load());
+ Q_ASSERT(d_2_1_Core->refs.loadRelaxed());
}
if (d_3_0_Core) {
d_3_0_Core->refs.deref();
- Q_ASSERT(d_3_0_Core->refs.load());
+ Q_ASSERT(d_3_0_Core->refs.loadRelaxed());
}
if (d_3_1_Core) {
d_3_1_Core->refs.deref();
- Q_ASSERT(d_3_1_Core->refs.load());
+ Q_ASSERT(d_3_1_Core->refs.loadRelaxed());
}
if (d_3_2_Core) {
d_3_2_Core->refs.deref();
- Q_ASSERT(d_3_2_Core->refs.load());
+ Q_ASSERT(d_3_2_Core->refs.loadRelaxed());
}
if (d_3_3_Core) {
d_3_3_Core->refs.deref();
- Q_ASSERT(d_3_3_Core->refs.load());
+ Q_ASSERT(d_3_3_Core->refs.loadRelaxed());
}
if (d_4_0_Core) {
d_4_0_Core->refs.deref();
- Q_ASSERT(d_4_0_Core->refs.load());
+ Q_ASSERT(d_4_0_Core->refs.loadRelaxed());
}
if (d_4_1_Core) {
d_4_1_Core->refs.deref();
- Q_ASSERT(d_4_1_Core->refs.load());
+ Q_ASSERT(d_4_1_Core->refs.loadRelaxed());
}
if (d_4_2_Core) {
d_4_2_Core->refs.deref();
- Q_ASSERT(d_4_2_Core->refs.load());
+ Q_ASSERT(d_4_2_Core->refs.loadRelaxed());
}
if (d_1_0_Deprecated) {
d_1_0_Deprecated->refs.deref();
- Q_ASSERT(d_1_0_Deprecated->refs.load());
+ Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed());
}
if (d_1_1_Deprecated) {
d_1_1_Deprecated->refs.deref();
- Q_ASSERT(d_1_1_Deprecated->refs.load());
+ Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed());
}
if (d_1_2_Deprecated) {
d_1_2_Deprecated->refs.deref();
- Q_ASSERT(d_1_2_Deprecated->refs.load());
+ Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed());
}
if (d_1_3_Deprecated) {
d_1_3_Deprecated->refs.deref();
- Q_ASSERT(d_1_3_Deprecated->refs.load());
+ Q_ASSERT(d_1_3_Deprecated->refs.loadRelaxed());
}
if (d_1_4_Deprecated) {
d_1_4_Deprecated->refs.deref();
- Q_ASSERT(d_1_4_Deprecated->refs.load());
+ Q_ASSERT(d_1_4_Deprecated->refs.loadRelaxed());
}
if (d_3_3_Deprecated) {
d_3_3_Deprecated->refs.deref();
- Q_ASSERT(d_3_3_Deprecated->refs.load());
+ Q_ASSERT(d_3_3_Deprecated->refs.loadRelaxed());
}
}
diff --git a/src/gui/opengl/qopenglfunctions_4_2_core.cpp b/src/gui/opengl/qopenglfunctions_4_2_core.cpp
index fdfb4db455..1381236926 100644
--- a/src/gui/opengl/qopenglfunctions_4_2_core.cpp
+++ b/src/gui/opengl/qopenglfunctions_4_2_core.cpp
@@ -89,63 +89,63 @@ QOpenGLFunctions_4_2_Core::~QOpenGLFunctions_4_2_Core()
{
if (d_1_0_Core) {
d_1_0_Core->refs.deref();
- Q_ASSERT(d_1_0_Core->refs.load());
+ Q_ASSERT(d_1_0_Core->refs.loadRelaxed());
}
if (d_1_1_Core) {
d_1_1_Core->refs.deref();
- Q_ASSERT(d_1_1_Core->refs.load());
+ Q_ASSERT(d_1_1_Core->refs.loadRelaxed());
}
if (d_1_2_Core) {
d_1_2_Core->refs.deref();
- Q_ASSERT(d_1_2_Core->refs.load());
+ Q_ASSERT(d_1_2_Core->refs.loadRelaxed());
}
if (d_1_3_Core) {
d_1_3_Core->refs.deref();
- Q_ASSERT(d_1_3_Core->refs.load());
+ Q_ASSERT(d_1_3_Core->refs.loadRelaxed());
}
if (d_1_4_Core) {
d_1_4_Core->refs.deref();
- Q_ASSERT(d_1_4_Core->refs.load());
+ Q_ASSERT(d_1_4_Core->refs.loadRelaxed());
}
if (d_1_5_Core) {
d_1_5_Core->refs.deref();
- Q_ASSERT(d_1_5_Core->refs.load());
+ Q_ASSERT(d_1_5_Core->refs.loadRelaxed());
}
if (d_2_0_Core) {
d_2_0_Core->refs.deref();
- Q_ASSERT(d_2_0_Core->refs.load());
+ Q_ASSERT(d_2_0_Core->refs.loadRelaxed());
}
if (d_2_1_Core) {
d_2_1_Core->refs.deref();
- Q_ASSERT(d_2_1_Core->refs.load());
+ Q_ASSERT(d_2_1_Core->refs.loadRelaxed());
}
if (d_3_0_Core) {
d_3_0_Core->refs.deref();
- Q_ASSERT(d_3_0_Core->refs.load());
+ Q_ASSERT(d_3_0_Core->refs.loadRelaxed());
}
if (d_3_1_Core) {
d_3_1_Core->refs.deref();
- Q_ASSERT(d_3_1_Core->refs.load());
+ Q_ASSERT(d_3_1_Core->refs.loadRelaxed());
}
if (d_3_2_Core) {
d_3_2_Core->refs.deref();
- Q_ASSERT(d_3_2_Core->refs.load());
+ Q_ASSERT(d_3_2_Core->refs.loadRelaxed());
}
if (d_3_3_Core) {
d_3_3_Core->refs.deref();
- Q_ASSERT(d_3_3_Core->refs.load());
+ Q_ASSERT(d_3_3_Core->refs.loadRelaxed());
}
if (d_4_0_Core) {
d_4_0_Core->refs.deref();
- Q_ASSERT(d_4_0_Core->refs.load());
+ Q_ASSERT(d_4_0_Core->refs.loadRelaxed());
}
if (d_4_1_Core) {
d_4_1_Core->refs.deref();
- Q_ASSERT(d_4_1_Core->refs.load());
+ Q_ASSERT(d_4_1_Core->refs.loadRelaxed());
}
if (d_4_2_Core) {
d_4_2_Core->refs.deref();
- Q_ASSERT(d_4_2_Core->refs.load());
+ Q_ASSERT(d_4_2_Core->refs.loadRelaxed());
}
}
diff --git a/src/gui/opengl/qopenglfunctions_4_3_compatibility.cpp b/src/gui/opengl/qopenglfunctions_4_3_compatibility.cpp
index 131ebc810f..5c0c711d1c 100644
--- a/src/gui/opengl/qopenglfunctions_4_3_compatibility.cpp
+++ b/src/gui/opengl/qopenglfunctions_4_3_compatibility.cpp
@@ -97,91 +97,91 @@ QOpenGLFunctions_4_3_Compatibility::~QOpenGLFunctions_4_3_Compatibility()
{
if (d_1_0_Core) {
d_1_0_Core->refs.deref();
- Q_ASSERT(d_1_0_Core->refs.load());
+ Q_ASSERT(d_1_0_Core->refs.loadRelaxed());
}
if (d_1_1_Core) {
d_1_1_Core->refs.deref();
- Q_ASSERT(d_1_1_Core->refs.load());
+ Q_ASSERT(d_1_1_Core->refs.loadRelaxed());
}
if (d_1_2_Core) {
d_1_2_Core->refs.deref();
- Q_ASSERT(d_1_2_Core->refs.load());
+ Q_ASSERT(d_1_2_Core->refs.loadRelaxed());
}
if (d_1_3_Core) {
d_1_3_Core->refs.deref();
- Q_ASSERT(d_1_3_Core->refs.load());
+ Q_ASSERT(d_1_3_Core->refs.loadRelaxed());
}
if (d_1_4_Core) {
d_1_4_Core->refs.deref();
- Q_ASSERT(d_1_4_Core->refs.load());
+ Q_ASSERT(d_1_4_Core->refs.loadRelaxed());
}
if (d_1_5_Core) {
d_1_5_Core->refs.deref();
- Q_ASSERT(d_1_5_Core->refs.load());
+ Q_ASSERT(d_1_5_Core->refs.loadRelaxed());
}
if (d_2_0_Core) {
d_2_0_Core->refs.deref();
- Q_ASSERT(d_2_0_Core->refs.load());
+ Q_ASSERT(d_2_0_Core->refs.loadRelaxed());
}
if (d_2_1_Core) {
d_2_1_Core->refs.deref();
- Q_ASSERT(d_2_1_Core->refs.load());
+ Q_ASSERT(d_2_1_Core->refs.loadRelaxed());
}
if (d_3_0_Core) {
d_3_0_Core->refs.deref();
- Q_ASSERT(d_3_0_Core->refs.load());
+ Q_ASSERT(d_3_0_Core->refs.loadRelaxed());
}
if (d_3_1_Core) {
d_3_1_Core->refs.deref();
- Q_ASSERT(d_3_1_Core->refs.load());
+ Q_ASSERT(d_3_1_Core->refs.loadRelaxed());
}
if (d_3_2_Core) {
d_3_2_Core->refs.deref();
- Q_ASSERT(d_3_2_Core->refs.load());
+ Q_ASSERT(d_3_2_Core->refs.loadRelaxed());
}
if (d_3_3_Core) {
d_3_3_Core->refs.deref();
- Q_ASSERT(d_3_3_Core->refs.load());
+ Q_ASSERT(d_3_3_Core->refs.loadRelaxed());
}
if (d_4_0_Core) {
d_4_0_Core->refs.deref();
- Q_ASSERT(d_4_0_Core->refs.load());
+ Q_ASSERT(d_4_0_Core->refs.loadRelaxed());
}
if (d_4_1_Core) {
d_4_1_Core->refs.deref();
- Q_ASSERT(d_4_1_Core->refs.load());
+ Q_ASSERT(d_4_1_Core->refs.loadRelaxed());
}
if (d_4_2_Core) {
d_4_2_Core->refs.deref();
- Q_ASSERT(d_4_2_Core->refs.load());
+ Q_ASSERT(d_4_2_Core->refs.loadRelaxed());
}
if (d_4_3_Core) {
d_4_3_Core->refs.deref();
- Q_ASSERT(d_4_3_Core->refs.load());
+ Q_ASSERT(d_4_3_Core->refs.loadRelaxed());
}
if (d_1_0_Deprecated) {
d_1_0_Deprecated->refs.deref();
- Q_ASSERT(d_1_0_Deprecated->refs.load());
+ Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed());
}
if (d_1_1_Deprecated) {
d_1_1_Deprecated->refs.deref();
- Q_ASSERT(d_1_1_Deprecated->refs.load());
+ Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed());
}
if (d_1_2_Deprecated) {
d_1_2_Deprecated->refs.deref();
- Q_ASSERT(d_1_2_Deprecated->refs.load());
+ Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed());
}
if (d_1_3_Deprecated) {
d_1_3_Deprecated->refs.deref();
- Q_ASSERT(d_1_3_Deprecated->refs.load());
+ Q_ASSERT(d_1_3_Deprecated->refs.loadRelaxed());
}
if (d_1_4_Deprecated) {
d_1_4_Deprecated->refs.deref();
- Q_ASSERT(d_1_4_Deprecated->refs.load());
+ Q_ASSERT(d_1_4_Deprecated->refs.loadRelaxed());
}
if (d_3_3_Deprecated) {
d_3_3_Deprecated->refs.deref();
- Q_ASSERT(d_3_3_Deprecated->refs.load());
+ Q_ASSERT(d_3_3_Deprecated->refs.loadRelaxed());
}
}
diff --git a/src/gui/opengl/qopenglfunctions_4_3_core.cpp b/src/gui/opengl/qopenglfunctions_4_3_core.cpp
index 95e2d7bc43..34460b841e 100644
--- a/src/gui/opengl/qopenglfunctions_4_3_core.cpp
+++ b/src/gui/opengl/qopenglfunctions_4_3_core.cpp
@@ -90,67 +90,67 @@ QOpenGLFunctions_4_3_Core::~QOpenGLFunctions_4_3_Core()
{
if (d_1_0_Core) {
d_1_0_Core->refs.deref();
- Q_ASSERT(d_1_0_Core->refs.load());
+ Q_ASSERT(d_1_0_Core->refs.loadRelaxed());
}
if (d_1_1_Core) {
d_1_1_Core->refs.deref();
- Q_ASSERT(d_1_1_Core->refs.load());
+ Q_ASSERT(d_1_1_Core->refs.loadRelaxed());
}
if (d_1_2_Core) {
d_1_2_Core->refs.deref();
- Q_ASSERT(d_1_2_Core->refs.load());
+ Q_ASSERT(d_1_2_Core->refs.loadRelaxed());
}
if (d_1_3_Core) {
d_1_3_Core->refs.deref();
- Q_ASSERT(d_1_3_Core->refs.load());
+ Q_ASSERT(d_1_3_Core->refs.loadRelaxed());
}
if (d_1_4_Core) {
d_1_4_Core->refs.deref();
- Q_ASSERT(d_1_4_Core->refs.load());
+ Q_ASSERT(d_1_4_Core->refs.loadRelaxed());
}
if (d_1_5_Core) {
d_1_5_Core->refs.deref();
- Q_ASSERT(d_1_5_Core->refs.load());
+ Q_ASSERT(d_1_5_Core->refs.loadRelaxed());
}
if (d_2_0_Core) {
d_2_0_Core->refs.deref();
- Q_ASSERT(d_2_0_Core->refs.load());
+ Q_ASSERT(d_2_0_Core->refs.loadRelaxed());
}
if (d_2_1_Core) {
d_2_1_Core->refs.deref();
- Q_ASSERT(d_2_1_Core->refs.load());
+ Q_ASSERT(d_2_1_Core->refs.loadRelaxed());
}
if (d_3_0_Core) {
d_3_0_Core->refs.deref();
- Q_ASSERT(d_3_0_Core->refs.load());
+ Q_ASSERT(d_3_0_Core->refs.loadRelaxed());
}
if (d_3_1_Core) {
d_3_1_Core->refs.deref();
- Q_ASSERT(d_3_1_Core->refs.load());
+ Q_ASSERT(d_3_1_Core->refs.loadRelaxed());
}
if (d_3_2_Core) {
d_3_2_Core->refs.deref();
- Q_ASSERT(d_3_2_Core->refs.load());
+ Q_ASSERT(d_3_2_Core->refs.loadRelaxed());
}
if (d_3_3_Core) {
d_3_3_Core->refs.deref();
- Q_ASSERT(d_3_3_Core->refs.load());
+ Q_ASSERT(d_3_3_Core->refs.loadRelaxed());
}
if (d_4_0_Core) {
d_4_0_Core->refs.deref();
- Q_ASSERT(d_4_0_Core->refs.load());
+ Q_ASSERT(d_4_0_Core->refs.loadRelaxed());
}
if (d_4_1_Core) {
d_4_1_Core->refs.deref();
- Q_ASSERT(d_4_1_Core->refs.load());
+ Q_ASSERT(d_4_1_Core->refs.loadRelaxed());
}
if (d_4_2_Core) {
d_4_2_Core->refs.deref();
- Q_ASSERT(d_4_2_Core->refs.load());
+ Q_ASSERT(d_4_2_Core->refs.loadRelaxed());
}
if (d_4_3_Core) {
d_4_3_Core->refs.deref();
- Q_ASSERT(d_4_3_Core->refs.load());
+ Q_ASSERT(d_4_3_Core->refs.loadRelaxed());
}
}
diff --git a/src/gui/opengl/qopenglfunctions_4_4_compatibility.cpp b/src/gui/opengl/qopenglfunctions_4_4_compatibility.cpp
index 5c7170b8fa..907994a3c4 100644
--- a/src/gui/opengl/qopenglfunctions_4_4_compatibility.cpp
+++ b/src/gui/opengl/qopenglfunctions_4_4_compatibility.cpp
@@ -97,95 +97,95 @@ QOpenGLFunctions_4_4_Compatibility::~QOpenGLFunctions_4_4_Compatibility()
{
if (d_1_0_Core) {
d_1_0_Core->refs.deref();
- Q_ASSERT(d_1_0_Core->refs.load());
+ Q_ASSERT(d_1_0_Core->refs.loadRelaxed());
}
if (d_1_1_Core) {
d_1_1_Core->refs.deref();
- Q_ASSERT(d_1_1_Core->refs.load());
+ Q_ASSERT(d_1_1_Core->refs.loadRelaxed());
}
if (d_1_2_Core) {
d_1_2_Core->refs.deref();
- Q_ASSERT(d_1_2_Core->refs.load());
+ Q_ASSERT(d_1_2_Core->refs.loadRelaxed());
}
if (d_1_3_Core) {
d_1_3_Core->refs.deref();
- Q_ASSERT(d_1_3_Core->refs.load());
+ Q_ASSERT(d_1_3_Core->refs.loadRelaxed());
}
if (d_1_4_Core) {
d_1_4_Core->refs.deref();
- Q_ASSERT(d_1_4_Core->refs.load());
+ Q_ASSERT(d_1_4_Core->refs.loadRelaxed());
}
if (d_1_5_Core) {
d_1_5_Core->refs.deref();
- Q_ASSERT(d_1_5_Core->refs.load());
+ Q_ASSERT(d_1_5_Core->refs.loadRelaxed());
}
if (d_2_0_Core) {
d_2_0_Core->refs.deref();
- Q_ASSERT(d_2_0_Core->refs.load());
+ Q_ASSERT(d_2_0_Core->refs.loadRelaxed());
}
if (d_2_1_Core) {
d_2_1_Core->refs.deref();
- Q_ASSERT(d_2_1_Core->refs.load());
+ Q_ASSERT(d_2_1_Core->refs.loadRelaxed());
}
if (d_3_0_Core) {
d_3_0_Core->refs.deref();
- Q_ASSERT(d_3_0_Core->refs.load());
+ Q_ASSERT(d_3_0_Core->refs.loadRelaxed());
}
if (d_3_1_Core) {
d_3_1_Core->refs.deref();
- Q_ASSERT(d_3_1_Core->refs.load());
+ Q_ASSERT(d_3_1_Core->refs.loadRelaxed());
}
if (d_3_2_Core) {
d_3_2_Core->refs.deref();
- Q_ASSERT(d_3_2_Core->refs.load());
+ Q_ASSERT(d_3_2_Core->refs.loadRelaxed());
}
if (d_3_3_Core) {
d_3_3_Core->refs.deref();
- Q_ASSERT(d_3_3_Core->refs.load());
+ Q_ASSERT(d_3_3_Core->refs.loadRelaxed());
}
if (d_4_0_Core) {
d_4_0_Core->refs.deref();
- Q_ASSERT(d_4_0_Core->refs.load());
+ Q_ASSERT(d_4_0_Core->refs.loadRelaxed());
}
if (d_4_1_Core) {
d_4_1_Core->refs.deref();
- Q_ASSERT(d_4_1_Core->refs.load());
+ Q_ASSERT(d_4_1_Core->refs.loadRelaxed());
}
if (d_4_2_Core) {
d_4_2_Core->refs.deref();
- Q_ASSERT(d_4_2_Core->refs.load());
+ Q_ASSERT(d_4_2_Core->refs.loadRelaxed());
}
if (d_4_3_Core) {
d_4_3_Core->refs.deref();
- Q_ASSERT(d_4_3_Core->refs.load());
+ Q_ASSERT(d_4_3_Core->refs.loadRelaxed());
}
if (d_4_4_Core) {
d_4_4_Core->refs.deref();
- Q_ASSERT(d_4_4_Core->refs.load());
+ Q_ASSERT(d_4_4_Core->refs.loadRelaxed());
}
if (d_1_0_Deprecated) {
d_1_0_Deprecated->refs.deref();
- Q_ASSERT(d_1_0_Deprecated->refs.load());
+ Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed());
}
if (d_1_1_Deprecated) {
d_1_1_Deprecated->refs.deref();
- Q_ASSERT(d_1_1_Deprecated->refs.load());
+ Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed());
}
if (d_1_2_Deprecated) {
d_1_2_Deprecated->refs.deref();
- Q_ASSERT(d_1_2_Deprecated->refs.load());
+ Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed());
}
if (d_1_3_Deprecated) {
d_1_3_Deprecated->refs.deref();
- Q_ASSERT(d_1_3_Deprecated->refs.load());
+ Q_ASSERT(d_1_3_Deprecated->refs.loadRelaxed());
}
if (d_1_4_Deprecated) {
d_1_4_Deprecated->refs.deref();
- Q_ASSERT(d_1_4_Deprecated->refs.load());
+ Q_ASSERT(d_1_4_Deprecated->refs.loadRelaxed());
}
if (d_3_3_Deprecated) {
d_3_3_Deprecated->refs.deref();
- Q_ASSERT(d_3_3_Deprecated->refs.load());
+ Q_ASSERT(d_3_3_Deprecated->refs.loadRelaxed());
}
}
diff --git a/src/gui/opengl/qopenglfunctions_4_4_core.cpp b/src/gui/opengl/qopenglfunctions_4_4_core.cpp
index 54833f9058..76c0323f6d 100644
--- a/src/gui/opengl/qopenglfunctions_4_4_core.cpp
+++ b/src/gui/opengl/qopenglfunctions_4_4_core.cpp
@@ -91,71 +91,71 @@ QOpenGLFunctions_4_4_Core::~QOpenGLFunctions_4_4_Core()
{
if (d_1_0_Core) {
d_1_0_Core->refs.deref();
- Q_ASSERT(d_1_0_Core->refs.load());
+ Q_ASSERT(d_1_0_Core->refs.loadRelaxed());
}
if (d_1_1_Core) {
d_1_1_Core->refs.deref();
- Q_ASSERT(d_1_1_Core->refs.load());
+ Q_ASSERT(d_1_1_Core->refs.loadRelaxed());
}
if (d_1_2_Core) {
d_1_2_Core->refs.deref();
- Q_ASSERT(d_1_2_Core->refs.load());
+ Q_ASSERT(d_1_2_Core->refs.loadRelaxed());
}
if (d_1_3_Core) {
d_1_3_Core->refs.deref();
- Q_ASSERT(d_1_3_Core->refs.load());
+ Q_ASSERT(d_1_3_Core->refs.loadRelaxed());
}
if (d_1_4_Core) {
d_1_4_Core->refs.deref();
- Q_ASSERT(d_1_4_Core->refs.load());
+ Q_ASSERT(d_1_4_Core->refs.loadRelaxed());
}
if (d_1_5_Core) {
d_1_5_Core->refs.deref();
- Q_ASSERT(d_1_5_Core->refs.load());
+ Q_ASSERT(d_1_5_Core->refs.loadRelaxed());
}
if (d_2_0_Core) {
d_2_0_Core->refs.deref();
- Q_ASSERT(d_2_0_Core->refs.load());
+ Q_ASSERT(d_2_0_Core->refs.loadRelaxed());
}
if (d_2_1_Core) {
d_2_1_Core->refs.deref();
- Q_ASSERT(d_2_1_Core->refs.load());
+ Q_ASSERT(d_2_1_Core->refs.loadRelaxed());
}
if (d_3_0_Core) {
d_3_0_Core->refs.deref();
- Q_ASSERT(d_3_0_Core->refs.load());
+ Q_ASSERT(d_3_0_Core->refs.loadRelaxed());
}
if (d_3_1_Core) {
d_3_1_Core->refs.deref();
- Q_ASSERT(d_3_1_Core->refs.load());
+ Q_ASSERT(d_3_1_Core->refs.loadRelaxed());
}
if (d_3_2_Core) {
d_3_2_Core->refs.deref();
- Q_ASSERT(d_3_2_Core->refs.load());
+ Q_ASSERT(d_3_2_Core->refs.loadRelaxed());
}
if (d_3_3_Core) {
d_3_3_Core->refs.deref();
- Q_ASSERT(d_3_3_Core->refs.load());
+ Q_ASSERT(d_3_3_Core->refs.loadRelaxed());
}
if (d_4_0_Core) {
d_4_0_Core->refs.deref();
- Q_ASSERT(d_4_0_Core->refs.load());
+ Q_ASSERT(d_4_0_Core->refs.loadRelaxed());
}
if (d_4_1_Core) {
d_4_1_Core->refs.deref();
- Q_ASSERT(d_4_1_Core->refs.load());
+ Q_ASSERT(d_4_1_Core->refs.loadRelaxed());
}
if (d_4_2_Core) {
d_4_2_Core->refs.deref();
- Q_ASSERT(d_4_2_Core->refs.load());
+ Q_ASSERT(d_4_2_Core->refs.loadRelaxed());
}
if (d_4_3_Core) {
d_4_3_Core->refs.deref();
- Q_ASSERT(d_4_3_Core->refs.load());
+ Q_ASSERT(d_4_3_Core->refs.loadRelaxed());
}
if (d_4_4_Core) {
d_4_4_Core->refs.deref();
- Q_ASSERT(d_4_4_Core->refs.load());
+ Q_ASSERT(d_4_4_Core->refs.loadRelaxed());
}
}
diff --git a/src/gui/opengl/qopenglfunctions_4_5_compatibility.cpp b/src/gui/opengl/qopenglfunctions_4_5_compatibility.cpp
index f9d2bb9ceb..c415bb06ff 100644
--- a/src/gui/opengl/qopenglfunctions_4_5_compatibility.cpp
+++ b/src/gui/opengl/qopenglfunctions_4_5_compatibility.cpp
@@ -99,103 +99,103 @@ QOpenGLFunctions_4_5_Compatibility::~QOpenGLFunctions_4_5_Compatibility()
{
if (d_1_0_Core) {
d_1_0_Core->refs.deref();
- Q_ASSERT(d_1_0_Core->refs.load());
+ Q_ASSERT(d_1_0_Core->refs.loadRelaxed());
}
if (d_1_1_Core) {
d_1_1_Core->refs.deref();
- Q_ASSERT(d_1_1_Core->refs.load());
+ Q_ASSERT(d_1_1_Core->refs.loadRelaxed());
}
if (d_1_2_Core) {
d_1_2_Core->refs.deref();
- Q_ASSERT(d_1_2_Core->refs.load());
+ Q_ASSERT(d_1_2_Core->refs.loadRelaxed());
}
if (d_1_3_Core) {
d_1_3_Core->refs.deref();
- Q_ASSERT(d_1_3_Core->refs.load());
+ Q_ASSERT(d_1_3_Core->refs.loadRelaxed());
}
if (d_1_4_Core) {
d_1_4_Core->refs.deref();
- Q_ASSERT(d_1_4_Core->refs.load());
+ Q_ASSERT(d_1_4_Core->refs.loadRelaxed());
}
if (d_1_5_Core) {
d_1_5_Core->refs.deref();
- Q_ASSERT(d_1_5_Core->refs.load());
+ Q_ASSERT(d_1_5_Core->refs.loadRelaxed());
}
if (d_2_0_Core) {
d_2_0_Core->refs.deref();
- Q_ASSERT(d_2_0_Core->refs.load());
+ Q_ASSERT(d_2_0_Core->refs.loadRelaxed());
}
if (d_2_1_Core) {
d_2_1_Core->refs.deref();
- Q_ASSERT(d_2_1_Core->refs.load());
+ Q_ASSERT(d_2_1_Core->refs.loadRelaxed());
}
if (d_3_0_Core) {
d_3_0_Core->refs.deref();
- Q_ASSERT(d_3_0_Core->refs.load());
+ Q_ASSERT(d_3_0_Core->refs.loadRelaxed());
}
if (d_3_1_Core) {
d_3_1_Core->refs.deref();
- Q_ASSERT(d_3_1_Core->refs.load());
+ Q_ASSERT(d_3_1_Core->refs.loadRelaxed());
}
if (d_3_2_Core) {
d_3_2_Core->refs.deref();
- Q_ASSERT(d_3_2_Core->refs.load());
+ Q_ASSERT(d_3_2_Core->refs.loadRelaxed());
}
if (d_3_3_Core) {
d_3_3_Core->refs.deref();
- Q_ASSERT(d_3_3_Core->refs.load());
+ Q_ASSERT(d_3_3_Core->refs.loadRelaxed());
}
if (d_4_0_Core) {
d_4_0_Core->refs.deref();
- Q_ASSERT(d_4_0_Core->refs.load());
+ Q_ASSERT(d_4_0_Core->refs.loadRelaxed());
}
if (d_4_1_Core) {
d_4_1_Core->refs.deref();
- Q_ASSERT(d_4_1_Core->refs.load());
+ Q_ASSERT(d_4_1_Core->refs.loadRelaxed());
}
if (d_4_2_Core) {
d_4_2_Core->refs.deref();
- Q_ASSERT(d_4_2_Core->refs.load());
+ Q_ASSERT(d_4_2_Core->refs.loadRelaxed());
}
if (d_4_3_Core) {
d_4_3_Core->refs.deref();
- Q_ASSERT(d_4_3_Core->refs.load());
+ Q_ASSERT(d_4_3_Core->refs.loadRelaxed());
}
if (d_4_4_Core) {
d_4_4_Core->refs.deref();
- Q_ASSERT(d_4_4_Core->refs.load());
+ Q_ASSERT(d_4_4_Core->refs.loadRelaxed());
}
if (d_4_5_Core) {
d_4_5_Core->refs.deref();
- Q_ASSERT(d_4_5_Core->refs.load());
+ Q_ASSERT(d_4_5_Core->refs.loadRelaxed());
}
if (d_1_0_Deprecated) {
d_1_0_Deprecated->refs.deref();
- Q_ASSERT(d_1_0_Deprecated->refs.load());
+ Q_ASSERT(d_1_0_Deprecated->refs.loadRelaxed());
}
if (d_1_1_Deprecated) {
d_1_1_Deprecated->refs.deref();
- Q_ASSERT(d_1_1_Deprecated->refs.load());
+ Q_ASSERT(d_1_1_Deprecated->refs.loadRelaxed());
}
if (d_1_2_Deprecated) {
d_1_2_Deprecated->refs.deref();
- Q_ASSERT(d_1_2_Deprecated->refs.load());
+ Q_ASSERT(d_1_2_Deprecated->refs.loadRelaxed());
}
if (d_1_3_Deprecated) {
d_1_3_Deprecated->refs.deref();
- Q_ASSERT(d_1_3_Deprecated->refs.load());
+ Q_ASSERT(d_1_3_Deprecated->refs.loadRelaxed());
}
if (d_1_4_Deprecated) {
d_1_4_Deprecated->refs.deref();
- Q_ASSERT(d_1_4_Deprecated->refs.load());
+ Q_ASSERT(d_1_4_Deprecated->refs.loadRelaxed());
}
if (d_3_3_Deprecated) {
d_3_3_Deprecated->refs.deref();
- Q_ASSERT(d_3_3_Deprecated->refs.load());
+ Q_ASSERT(d_3_3_Deprecated->refs.loadRelaxed());
}
if (d_4_5_Deprecated) {
d_4_5_Deprecated->refs.deref();
- Q_ASSERT(d_4_5_Deprecated->refs.load());
+ Q_ASSERT(d_4_5_Deprecated->refs.loadRelaxed());
}
}
diff --git a/src/gui/opengl/qopenglfunctions_4_5_core.cpp b/src/gui/opengl/qopenglfunctions_4_5_core.cpp
index a47ebb9ee9..4dfac3579c 100644
--- a/src/gui/opengl/qopenglfunctions_4_5_core.cpp
+++ b/src/gui/opengl/qopenglfunctions_4_5_core.cpp
@@ -92,75 +92,75 @@ QOpenGLFunctions_4_5_Core::~QOpenGLFunctions_4_5_Core()
{
if (d_1_0_Core) {
d_1_0_Core->refs.deref();
- Q_ASSERT(d_1_0_Core->refs.load());
+ Q_ASSERT(d_1_0_Core->refs.loadRelaxed());
}
if (d_1_1_Core) {
d_1_1_Core->refs.deref();
- Q_ASSERT(d_1_1_Core->refs.load());
+ Q_ASSERT(d_1_1_Core->refs.loadRelaxed());
}
if (d_1_2_Core) {
d_1_2_Core->refs.deref();
- Q_ASSERT(d_1_2_Core->refs.load());
+ Q_ASSERT(d_1_2_Core->refs.loadRelaxed());
}
if (d_1_3_Core) {
d_1_3_Core->refs.deref();
- Q_ASSERT(d_1_3_Core->refs.load());
+ Q_ASSERT(d_1_3_Core->refs.loadRelaxed());
}
if (d_1_4_Core) {
d_1_4_Core->refs.deref();
- Q_ASSERT(d_1_4_Core->refs.load());
+ Q_ASSERT(d_1_4_Core->refs.loadRelaxed());
}
if (d_1_5_Core) {
d_1_5_Core->refs.deref();
- Q_ASSERT(d_1_5_Core->refs.load());
+ Q_ASSERT(d_1_5_Core->refs.loadRelaxed());
}
if (d_2_0_Core) {
d_2_0_Core->refs.deref();
- Q_ASSERT(d_2_0_Core->refs.load());
+ Q_ASSERT(d_2_0_Core->refs.loadRelaxed());
}
if (d_2_1_Core) {
d_2_1_Core->refs.deref();
- Q_ASSERT(d_2_1_Core->refs.load());
+ Q_ASSERT(d_2_1_Core->refs.loadRelaxed());
}
if (d_3_0_Core) {
d_3_0_Core->refs.deref();
- Q_ASSERT(d_3_0_Core->refs.load());
+ Q_ASSERT(d_3_0_Core->refs.loadRelaxed());
}
if (d_3_1_Core) {
d_3_1_Core->refs.deref();
- Q_ASSERT(d_3_1_Core->refs.load());
+ Q_ASSERT(d_3_1_Core->refs.loadRelaxed());
}
if (d_3_2_Core) {
d_3_2_Core->refs.deref();
- Q_ASSERT(d_3_2_Core->refs.load());
+ Q_ASSERT(d_3_2_Core->refs.loadRelaxed());
}
if (d_3_3_Core) {
d_3_3_Core->refs.deref();
- Q_ASSERT(d_3_3_Core->refs.load());
+ Q_ASSERT(d_3_3_Core->refs.loadRelaxed());
}
if (d_4_0_Core) {
d_4_0_Core->refs.deref();
- Q_ASSERT(d_4_0_Core->refs.load());
+ Q_ASSERT(d_4_0_Core->refs.loadRelaxed());
}
if (d_4_1_Core) {
d_4_1_Core->refs.deref();
- Q_ASSERT(d_4_1_Core->refs.load());
+ Q_ASSERT(d_4_1_Core->refs.loadRelaxed());
}
if (d_4_2_Core) {
d_4_2_Core->refs.deref();
- Q_ASSERT(d_4_2_Core->refs.load());
+ Q_ASSERT(d_4_2_Core->refs.loadRelaxed());
}
if (d_4_3_Core) {
d_4_3_Core->refs.deref();
- Q_ASSERT(d_4_3_Core->refs.load());
+ Q_ASSERT(d_4_3_Core->refs.loadRelaxed());
}
if (d_4_4_Core) {
d_4_4_Core->refs.deref();
- Q_ASSERT(d_4_4_Core->refs.load());
+ Q_ASSERT(d_4_4_Core->refs.loadRelaxed());
}
if (d_4_5_Core) {
d_4_5_Core->refs.deref();
- Q_ASSERT(d_4_5_Core->refs.load());
+ Q_ASSERT(d_4_5_Core->refs.loadRelaxed());
}
}
diff --git a/src/gui/opengl/qopengltextureblitter.cpp b/src/gui/opengl/qopengltextureblitter.cpp
index b65df9dc82..b709f2f639 100644
--- a/src/gui/opengl/qopengltextureblitter.cpp
+++ b/src/gui/opengl/qopengltextureblitter.cpp
@@ -349,6 +349,9 @@ bool QOpenGLTextureBlitterPrivate::buildProgram(ProgramIndex idx, const char *vs
p->glProgram->setUniformValue(p->swizzleUniformPos, false);
+ // minmize state left set after a create()
+ p->glProgram->release();
+
return true;
}
diff --git a/src/gui/painting/qbrush.cpp b/src/gui/painting/qbrush.cpp
index 21e41979af..abb3268dfa 100644
--- a/src/gui/painting/qbrush.cpp
+++ b/src/gui/painting/qbrush.cpp
@@ -352,7 +352,7 @@ public:
QBrushData *brush;
QNullBrushData() : brush(new QBrushData)
{
- brush->ref.store(1);
+ brush->ref.storeRelaxed(1);
brush->style = Qt::BrushStyle(0);
brush->color = Qt::black;
}
@@ -411,7 +411,7 @@ void QBrush::init(const QColor &color, Qt::BrushStyle style)
d.reset(new QBrushData);
break;
}
- d->ref.store(1);
+ d->ref.storeRelaxed(1);
d->style = style;
d->color = color;
}
@@ -585,7 +585,7 @@ static Q_DECL_CONSTEXPR inline bool use_same_brushdata(Qt::BrushStyle lhs, Qt::B
void QBrush::detach(Qt::BrushStyle newStyle)
{
- if (use_same_brushdata(newStyle, d->style) && d->ref.load() == 1) {
+ if (use_same_brushdata(newStyle, d->style) && d->ref.loadRelaxed() == 1) {
d->style = newStyle;
return;
}
@@ -625,7 +625,7 @@ void QBrush::detach(Qt::BrushStyle newStyle)
x.reset(new QBrushData);
break;
}
- x->ref.store(1); // must be first lest the QBrushDataPointerDeleter turns into a no-op
+ x->ref.storeRelaxed(1); // must be first lest the QBrushDataPointerDeleter turns into a no-op
x->style = newStyle;
x->color = d->color;
x->transform = d->transform;
diff --git a/src/gui/painting/qbrush.h b/src/gui/painting/qbrush.h
index ca51430cf4..6a4ffab1c5 100644
--- a/src/gui/painting/qbrush.h
+++ b/src/gui/painting/qbrush.h
@@ -159,7 +159,7 @@ inline Qt::BrushStyle QBrush::style() const { return d->style; }
inline const QColor &QBrush::color() const { return d->color; }
inline const QMatrix &QBrush::matrix() const { return d->transform.toAffine(); }
inline QTransform QBrush::transform() const { return d->transform; }
-inline bool QBrush::isDetached() const { return d->ref.load() == 1; }
+inline bool QBrush::isDetached() const { return d->ref.loadRelaxed() == 1; }
/*******************************************************************************
@@ -371,6 +371,8 @@ public:
GagarinView = 178,
FabledSunset = 179,
PerfectBlue = 180,
+
+ NumPresets
};
Q_ENUM(Preset)
diff --git a/src/gui/painting/qcolor.cpp b/src/gui/painting/qcolor.cpp
index 174350d884..6cbc30e79a 100644
--- a/src/gui/painting/qcolor.cpp
+++ b/src/gui/painting/qcolor.cpp
@@ -1349,7 +1349,7 @@ void QColor::setRgbF(qreal r, qreal g, qreal b, qreal a)
*/
void QColor::setRgb(int r, int g, int b, int a)
{
- if ((uint)r > 255 || (uint)g > 255 || (uint)b > 255 || (uint)a > 255) {
+ if (!isRgbaValid(r, g, b, a)) {
qWarning("QColor::setRgb: RGB parameters out of range");
invalidate();
return;
@@ -2398,10 +2398,7 @@ QColor QColor::fromRgba(QRgb rgba) noexcept
*/
QColor QColor::fromRgb(int r, int g, int b, int a)
{
- if (r < 0 || r > 255
- || g < 0 || g > 255
- || b < 0 || b > 255
- || a < 0 || a > 255) {
+ if (!isRgbaValid(r, g, b, a)) {
qWarning("QColor::fromRgb: RGB parameters out of range");
return QColor();
}
diff --git a/src/gui/painting/qcolor.h b/src/gui/painting/qcolor.h
index e3c267f97d..723b9fce73 100644
--- a/src/gui/painting/qcolor.h
+++ b/src/gui/painting/qcolor.h
@@ -67,9 +67,16 @@ public:
enum Spec { Invalid, Rgb, Hsv, Cmyk, Hsl, ExtendedRgb };
enum NameFormat { HexRgb, HexArgb };
- inline QColor() noexcept;
+ Q_DECL_CONSTEXPR QColor() noexcept
+ : cspec(Invalid), ct(USHRT_MAX, 0, 0, 0, 0) {}
QColor(Qt::GlobalColor color) noexcept;
- inline QColor(int r, int g, int b, int a = 255);
+ Q_DECL_CONSTEXPR QColor(int r, int g, int b, int a = 255) noexcept
+ : cspec(isRgbaValid(r, g, b, a) ? Rgb : Invalid),
+ ct(cspec == Rgb ? a * 0x0101 : 0,
+ cspec == Rgb ? r * 0x0101 : 0,
+ cspec == Rgb ? g * 0x0101 : 0,
+ cspec == Rgb ? b * 0x0101 : 0,
+ 0) {}
QColor(QRgb rgb) noexcept;
QColor(QRgba64 rgba64) noexcept;
#if QT_STRINGVIEW_LEVEL < 2
@@ -81,8 +88,11 @@ public:
QColor(Spec spec) noexcept;
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
- inline QColor(const QColor &color) noexcept; // ### Qt 6: remove all of these, the trivial ones are fine.
- QColor(QColor &&other) noexcept : cspec(other.cspec), ct(other.ct) {}
+ // ### Qt 6: remove all of these, the trivial ones are fine.
+ Q_DECL_CONSTEXPR QColor(const QColor &color) noexcept
+ : cspec(color.cspec), ct(color.ct)
+ {}
+ Q_DECL_CONSTEXPR QColor(QColor &&other) noexcept : cspec(other.cspec), ct(other.ct) {}
QColor &operator=(QColor &&other) noexcept
{ cspec = other.cspec; ct = other.ct; return *this; }
QColor &operator=(const QColor &) noexcept;
@@ -244,8 +254,18 @@ private:
template <typename String>
bool setColorFromString(String name);
+ static Q_DECL_CONSTEXPR bool isRgbaValid(int r, int g, int b, int a = 255) noexcept Q_DECL_CONST_FUNCTION
+ {
+ return uint(r) <= 255 && uint(g) <= 255 && uint(b) <= 255 && uint(a) <= 255;
+ }
+
Spec cspec;
- union {
+ union CT {
+#ifdef Q_COMPILER_UNIFORM_INIT
+ CT() {} // doesn't init anything, thus can't be constexpr
+ Q_DECL_CONSTEXPR explicit CT(ushort a1, ushort a2, ushort a3, ushort a4, ushort a5) noexcept
+ : array{a1, a2, a3, a4, a5} {}
+#endif
struct {
ushort alpha;
ushort red;
@@ -292,12 +312,6 @@ private:
};
Q_DECLARE_TYPEINFO(QColor, QT_VERSION >= QT_VERSION_CHECK(6,0,0) ? Q_MOVABLE_TYPE : Q_RELOCATABLE_TYPE);
-inline QColor::QColor() noexcept
-{ invalidate(); }
-
-inline QColor::QColor(int r, int g, int b, int a)
-{ setRgb(r, g, b, a); }
-
inline QColor::QColor(QLatin1String aname)
{ setNamedColor(aname); }
@@ -309,12 +323,6 @@ inline QColor::QColor(const QString& aname)
{ setNamedColor(aname); }
#endif
-#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
-inline QColor::QColor(const QColor &acolor) noexcept
- : cspec(acolor.cspec)
-{ ct.argb = acolor.ct.argb; }
-#endif
-
inline bool QColor::isValid() const noexcept
{ return cspec != Invalid; }
diff --git a/src/gui/painting/qcolorspace_p.h b/src/gui/painting/qcolorspace_p.h
index a49c46f195..95e0655d0c 100644
--- a/src/gui/painting/qcolorspace_p.h
+++ b/src/gui/painting/qcolorspace_p.h
@@ -123,7 +123,7 @@ public:
table[0] = other.table[0];
table[1] = other.table[1];
table[2] = other.table[2];
- generated.store(1);
+ generated.storeRelaxed(1);
}
}
QSharedPointer<QColorTrcLut> &operator[](int i) { return table[i]; }
diff --git a/src/gui/painting/qcolortransform.cpp b/src/gui/painting/qcolortransform.cpp
index 2f81449693..de08bf4221 100644
--- a/src/gui/painting/qcolortransform.cpp
+++ b/src/gui/painting/qcolortransform.cpp
@@ -71,7 +71,7 @@ void QColorTransformPrivate::updateLutsIn() const
if (colorSpaceIn->lut.generated.loadAcquire())
return;
QMutexLocker lock(&QColorSpacePrivate::s_lutWriteLock);
- if (colorSpaceIn->lut.generated.load())
+ if (colorSpaceIn->lut.generated.loadRelaxed())
return;
for (int i = 0; i < 3; ++i) {
@@ -96,7 +96,7 @@ void QColorTransformPrivate::updateLutsOut() const
if (colorSpaceOut->lut.generated.loadAcquire())
return;
QMutexLocker lock(&QColorSpacePrivate::s_lutWriteLock);
- if (colorSpaceOut->lut.generated.load())
+ if (colorSpaceOut->lut.generated.loadRelaxed())
return;
for (int i = 0; i < 3; ++i) {
if (!colorSpaceOut->trc[i].isValid())
diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp
index ac15d8f4f4..006befea22 100644
--- a/src/gui/painting/qdrawhelper.cpp
+++ b/src/gui/painting/qdrawhelper.cpp
@@ -4566,7 +4566,9 @@ static void blend_color_rgb16(int count, const QSpan *spans, void *userData)
if (mode == QPainter::CompositionMode_Source) {
// inline for performance
ushort c = data->solidColor.toRgb16();
- while (count--) {
+ for (; count--; spans++) {
+ if (!spans->len)
+ continue;
ushort *target = ((ushort *)data->rasterBuffer->scanLine(spans->y)) + spans->x;
if (spans->coverage == 255) {
qt_memfill(target, c, spans->len);
@@ -4579,13 +4581,14 @@ static void blend_color_rgb16(int count, const QSpan *spans, void *userData)
++target;
}
}
- ++spans;
}
return;
}
if (mode == QPainter::CompositionMode_SourceOver) {
- while (count--) {
+ for (; count--; spans++) {
+ if (!spans->len)
+ continue;
uint color = BYTE_MUL(data->solidColor.toArgb32(), spans->coverage);
int ialpha = qAlpha(~color);
ushort c = qConvertRgb32To16(color);
@@ -4617,7 +4620,6 @@ static void blend_color_rgb16(int count, const QSpan *spans, void *userData)
// one last pixel beyond a full word
*target = c + BYTE_MUL_RGB16(*target, ialpha);
}
- ++spans;
}
return;
}
@@ -4634,6 +4636,11 @@ void handleSpans(int count, const QSpan *spans, const QSpanData *data, T &handle
int coverage = 0;
while (count) {
+ if (!spans->len) {
+ ++spans;
+ --count;
+ continue;
+ }
int x = spans->x;
const int y = spans->y;
int right = x + spans->len;
@@ -4790,7 +4797,9 @@ static void blend_untransformed_generic(int count, const QSpan *spans, void *use
int xoff = -qRound(-data->dx);
int yoff = -qRound(-data->dy);
- while (count--) {
+ for (; count--; spans++) {
+ if (!spans->len)
+ continue;
int x = spans->x;
int length = spans->len;
int sx = xoff + x;
@@ -4818,7 +4827,6 @@ static void blend_untransformed_generic(int count, const QSpan *spans, void *use
}
}
}
- ++spans;
}
}
@@ -4840,7 +4848,9 @@ static void blend_untransformed_generic_rgb64(int count, const QSpan *spans, voi
int xoff = -qRound(-data->dx);
int yoff = -qRound(-data->dy);
- while (count--) {
+ for (; count--; spans++) {
+ if (!spans->len)
+ continue;
int x = spans->x;
int length = spans->len;
int sx = xoff + x;
@@ -4868,7 +4878,6 @@ static void blend_untransformed_generic_rgb64(int count, const QSpan *spans, voi
}
}
}
- ++spans;
}
}
#endif
@@ -4889,7 +4898,9 @@ static void blend_untransformed_argb(int count, const QSpan *spans, void *userDa
int xoff = -qRound(-data->dx);
int yoff = -qRound(-data->dy);
- while (count--) {
+ for (; count--; spans++) {
+ if (!spans->len)
+ continue;
int x = spans->x;
int length = spans->len;
int sx = xoff + x;
@@ -4909,7 +4920,6 @@ static void blend_untransformed_argb(int count, const QSpan *spans, void *userDa
op.func(dest, src, length, coverage);
}
}
- ++spans;
}
}
@@ -4982,7 +4992,12 @@ static void blend_untransformed_rgb565(int count, const QSpan *spans, void *user
int xoff = -qRound(-data->dx);
int yoff = -qRound(-data->dy);
- while (count--) {
+ const QSpan *end = spans + count;
+ while (spans < end) {
+ if (!spans->len) {
+ ++spans;
+ continue;
+ }
const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8;
if (coverage == 0) {
++spans;
diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp
index 85ddff53db..64183c2be6 100644
--- a/src/gui/painting/qpaintengine_raster.cpp
+++ b/src/gui/painting/qpaintengine_raster.cpp
@@ -4149,7 +4149,7 @@ static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userDa
Clip spans to \a{clip}-rectangle.
Returns number of unclipped spans
*/
-static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
+static int qt_intersect_spans(QT_FT_Span *&spans, int numSpans,
const QRect &clip)
{
const short minx = clip.left();
@@ -4157,29 +4157,32 @@ static int qt_intersect_spans(QT_FT_Span *spans, int numSpans,
const short maxx = clip.right();
const short maxy = clip.bottom();
- int n = 0;
- for (int i = 0; i < numSpans; ++i) {
- if (spans[i].y > maxy)
+ QT_FT_Span *end = spans + numSpans;
+ while (spans < end) {
+ if (spans->y >= miny)
break;
- if (spans[i].y < miny
- || spans[i].x > maxx
- || spans[i].x + spans[i].len <= minx) {
+ ++spans;
+ }
+
+ QT_FT_Span *s = spans;
+ while (s < end) {
+ if (s->y > maxy)
+ break;
+ if (s->x > maxx || s->x + s->len <= minx) {
+ s->len = 0;
+ ++s;
continue;
}
- if (spans[i].x < minx) {
- spans[n].len = qMin(spans[i].len - (minx - spans[i].x), maxx - minx + 1);
- spans[n].x = minx;
+ if (s->x < minx) {
+ s->len = qMin(s->len - (minx - s->x), maxx - minx + 1);
+ s->x = minx;
} else {
- spans[n].x = spans[i].x;
- spans[n].len = qMin(spans[i].len, ushort(maxx - spans[n].x + 1));
+ s->len = qMin(s->len, ushort(maxx - s->x + 1));
}
- if (spans[n].len == 0)
- continue;
- spans[n].y = spans[i].y;
- spans[n].coverage = spans[i].coverage;
- ++n;
+ ++s;
}
- return n;
+
+ return s - spans;
}
@@ -4192,11 +4195,12 @@ static void qt_span_fill_clipRect(int count, const QSpan *spans,
Q_ASSERT(fillData->clip);
Q_ASSERT(!fillData->clip->clipRect.isEmpty());
+ QSpan *s = const_cast<QSpan *>(spans);
// hw: check if this const_cast<> is safe!!!
- count = qt_intersect_spans(const_cast<QSpan*>(spans), count,
+ count = qt_intersect_spans(s, count,
fillData->clip->clipRect);
if (count > 0)
- fillData->unclipped_blend(count, spans, fillData);
+ fillData->unclipped_blend(count, s, fillData);
}
static void qt_span_clip(int count, const QSpan *spans, void *userData)
@@ -4773,7 +4777,8 @@ static inline void drawEllipsePoints(int x, int y, int length,
if (length == 0)
return;
- QT_FT_Span outline[4];
+ QT_FT_Span _outline[4];
+ QT_FT_Span *outline = _outline;
const int midx = rect.x() + (rect.width() + 1) / 2;
const int midy = rect.y() + (rect.height() + 1) / 2;
@@ -4805,7 +4810,8 @@ static inline void drawEllipsePoints(int x, int y, int length,
outline[3].coverage = 255;
if (brush_func && outline[0].x + outline[0].len < outline[1].x) {
- QT_FT_Span fill[2];
+ QT_FT_Span _fill[2];
+ QT_FT_Span *fill = _fill;
// top fill
fill[0].x = outline[0].x + outline[0].len - 1;
diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp
index 95e6bda78b..84b34e390b 100644
--- a/src/gui/painting/qpainter.cpp
+++ b/src/gui/painting/qpainter.cpp
@@ -283,7 +283,7 @@ bool QPainterPrivate::attachPainterPrivate(QPainter *q, QPaintDevice *pdev)
Q_ASSERT(q->d_ptr->state);
// Now initialize the painter with correct widget properties.
- q->initFrom(pdev);
+ q->d_ptr->initFrom(pdev);
QPoint offset;
pdev->redirected(&offset);
offset += q->d_ptr->engine->coordinateOffset();
@@ -1560,22 +1560,28 @@ void QPainter::initFrom(const QPaintDevice *device)
{
Q_ASSERT_X(device, "QPainter::initFrom(const QPaintDevice *device)", "QPaintDevice cannot be 0");
Q_D(QPainter);
- if (!d->engine) {
+ d->initFrom(device);
+}
+#endif
+
+void QPainterPrivate::initFrom(const QPaintDevice *device)
+{
+ if (!engine) {
qWarning("QPainter::initFrom: Painter not active, aborted");
return;
}
- device->initPainter(this);
+ Q_Q(QPainter);
+ device->initPainter(q);
- if (d->extended) {
- d->extended->penChanged();
- } else if (d->engine) {
- d->engine->setDirty(QPaintEngine::DirtyPen);
- d->engine->setDirty(QPaintEngine::DirtyBrush);
- d->engine->setDirty(QPaintEngine::DirtyFont);
+ if (extended) {
+ extended->penChanged();
+ } else if (engine) {
+ engine->setDirty(QPaintEngine::DirtyPen);
+ engine->setDirty(QPaintEngine::DirtyBrush);
+ engine->setDirty(QPaintEngine::DirtyFont);
}
}
-#endif
/*!
Saves the current painter state (pushes the state onto a stack). A
@@ -1843,7 +1849,7 @@ bool QPainter::begin(QPaintDevice *pd)
// Copy painter properties from original paint device,
// required for QPixmap::grabWidget()
if (d->original_device->devType() == QInternal::Widget) {
- initFrom(d->original_device);
+ d->initFrom(d->original_device);
} else {
d->state->layoutDirection = Qt::LayoutDirectionAuto;
// make sure we have a font compatible with the paintdevice
diff --git a/src/gui/painting/qpainter_p.h b/src/gui/painting/qpainter_p.h
index bd2bc4c9b3..29d4880eb9 100644
--- a/src/gui/painting/qpainter_p.h
+++ b/src/gui/painting/qpainter_p.h
@@ -256,6 +256,7 @@ public:
QTransform hidpiScaleTransform() const;
static bool attachPainterPrivate(QPainter *q, QPaintDevice *pdev);
void detachPainterPrivate(QPainter *q);
+ void initFrom(const QPaintDevice *device);
QPaintDevice *device;
QPaintDevice *original_device;
diff --git a/src/gui/painting/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp
index 42872359d7..70ad6bdc97 100644
--- a/src/gui/painting/qpainterpath.cpp
+++ b/src/gui/painting/qpainterpath.cpp
@@ -71,6 +71,24 @@
QT_BEGIN_NAMESPACE
+static inline bool isValidCoord(qreal c)
+{
+ if (sizeof(qreal) >= sizeof(double))
+ return qIsFinite(c) && fabs(c) < 1e128;
+ else
+ return qIsFinite(c) && fabsf(float(c)) < 1e16f;
+}
+
+static bool hasValidCoords(QPointF p)
+{
+ return isValidCoord(p.x()) && isValidCoord(p.y());
+}
+
+static bool hasValidCoords(QRectF r)
+{
+ return isValidCoord(r.x()) && isValidCoord(r.y()) && isValidCoord(r.width()) && isValidCoord(r.height());
+}
+
struct QPainterPathPrivateDeleter
{
static inline void cleanup(QPainterPathPrivate *d)
@@ -560,7 +578,7 @@ QPainterPath::QPainterPath(const QPointF &startPoint)
void QPainterPath::detach()
{
- if (d_ptr->ref.load() != 1)
+ if (d_ptr->ref.loadRelaxed() != 1)
detach_helper();
setDirty(true);
}
@@ -724,9 +742,9 @@ void QPainterPath::moveTo(const QPointF &p)
printf("QPainterPath::moveTo() (%.2f,%.2f)\n", p.x(), p.y());
#endif
- if (!qt_is_finite(p.x()) || !qt_is_finite(p.y())) {
+ if (!hasValidCoords(p)) {
#ifndef QT_NO_DEBUG
- qWarning("QPainterPath::moveTo: Adding point where x or y is NaN or Inf, ignoring call");
+ qWarning("QPainterPath::moveTo: Adding point with invalid coordinates, ignoring call");
#endif
return;
}
@@ -774,9 +792,9 @@ void QPainterPath::lineTo(const QPointF &p)
printf("QPainterPath::lineTo() (%.2f,%.2f)\n", p.x(), p.y());
#endif
- if (!qt_is_finite(p.x()) || !qt_is_finite(p.y())) {
+ if (!hasValidCoords(p)) {
#ifndef QT_NO_DEBUG
- qWarning("QPainterPath::lineTo: Adding point where x or y is NaN or Inf, ignoring call");
+ qWarning("QPainterPath::lineTo: Adding point with invalid coordinates, ignoring call");
#endif
return;
}
@@ -833,10 +851,9 @@ void QPainterPath::cubicTo(const QPointF &c1, const QPointF &c2, const QPointF &
c1.x(), c1.y(), c2.x(), c2.y(), e.x(), e.y());
#endif
- if (!qt_is_finite(c1.x()) || !qt_is_finite(c1.y()) || !qt_is_finite(c2.x()) || !qt_is_finite(c2.y())
- || !qt_is_finite(e.x()) || !qt_is_finite(e.y())) {
+ if (!hasValidCoords(c1) || !hasValidCoords(c2) || !hasValidCoords(e)) {
#ifndef QT_NO_DEBUG
- qWarning("QPainterPath::cubicTo: Adding point where x or y is NaN or Inf, ignoring call");
+ qWarning("QPainterPath::cubicTo: Adding point with invalid coordinates, ignoring call");
#endif
return;
}
@@ -890,9 +907,9 @@ void QPainterPath::quadTo(const QPointF &c, const QPointF &e)
c.x(), c.y(), e.x(), e.y());
#endif
- if (!qt_is_finite(c.x()) || !qt_is_finite(c.y()) || !qt_is_finite(e.x()) || !qt_is_finite(e.y())) {
+ if (!hasValidCoords(c) || !hasValidCoords(e)) {
#ifndef QT_NO_DEBUG
- qWarning("QPainterPath::quadTo: Adding point where x or y is NaN or Inf, ignoring call");
+ qWarning("QPainterPath::quadTo: Adding point with invalid coordinates, ignoring call");
#endif
return;
}
@@ -961,10 +978,9 @@ void QPainterPath::arcTo(const QRectF &rect, qreal startAngle, qreal sweepLength
rect.x(), rect.y(), rect.width(), rect.height(), startAngle, sweepLength);
#endif
- if ((!qt_is_finite(rect.x()) && !qt_is_finite(rect.y())) || !qt_is_finite(rect.width()) || !qt_is_finite(rect.height())
- || !qt_is_finite(startAngle) || !qt_is_finite(sweepLength)) {
+ if (!hasValidCoords(rect) || !isValidCoord(startAngle) || !isValidCoord(sweepLength)) {
#ifndef QT_NO_DEBUG
- qWarning("QPainterPath::arcTo: Adding arc where a parameter is NaN or Inf, ignoring call");
+ qWarning("QPainterPath::arcTo: Adding point with invalid coordinates, ignoring call");
#endif
return;
}
@@ -1067,9 +1083,9 @@ QPointF QPainterPath::currentPosition() const
*/
void QPainterPath::addRect(const QRectF &r)
{
- if (!qt_is_finite(r.x()) || !qt_is_finite(r.y()) || !qt_is_finite(r.width()) || !qt_is_finite(r.height())) {
+ if (!hasValidCoords(r)) {
#ifndef QT_NO_DEBUG
- qWarning("QPainterPath::addRect: Adding rect where a parameter is NaN or Inf, ignoring call");
+ qWarning("QPainterPath::addRect: Adding point with invalid coordinates, ignoring call");
#endif
return;
}
@@ -1147,10 +1163,9 @@ void QPainterPath::addPolygon(const QPolygonF &polygon)
*/
void QPainterPath::addEllipse(const QRectF &boundingRect)
{
- if (!qt_is_finite(boundingRect.x()) || !qt_is_finite(boundingRect.y())
- || !qt_is_finite(boundingRect.width()) || !qt_is_finite(boundingRect.height())) {
+ if (!hasValidCoords(boundingRect)) {
#ifndef QT_NO_DEBUG
- qWarning("QPainterPath::addEllipse: Adding ellipse where a parameter is NaN or Inf, ignoring call");
+ qWarning("QPainterPath::addEllipse: Adding point with invalid coordinates, ignoring call");
#endif
return;
}
@@ -2501,6 +2516,7 @@ QDataStream &operator<<(QDataStream &s, const QPainterPath &p)
*/
QDataStream &operator>>(QDataStream &s, QPainterPath &p)
{
+ bool errorDetected = false;
int size;
s >> size;
@@ -2519,10 +2535,11 @@ QDataStream &operator>>(QDataStream &s, QPainterPath &p)
s >> x;
s >> y;
Q_ASSERT(type >= 0 && type <= 3);
- if (!qt_is_finite(x) || !qt_is_finite(y)) {
+ if (!isValidCoord(qreal(x)) || !isValidCoord(qreal(y))) {
#ifndef QT_NO_DEBUG
- qWarning("QDataStream::operator>>: NaN or Inf element found in path, skipping it");
+ qWarning("QDataStream::operator>>: Invalid QPainterPath coordinates read, skipping it");
#endif
+ errorDetected = true;
continue;
}
QPainterPath::Element elm = { qreal(x), qreal(y), QPainterPath::ElementType(type) };
@@ -2535,6 +2552,8 @@ QDataStream &operator>>(QDataStream &s, QPainterPath &p)
p.d_func()->fillRule = Qt::FillRule(fillRule);
p.d_func()->dirtyBounds = true;
p.d_func()->dirtyControlBounds = true;
+ if (errorDetected)
+ p = QPainterPath(); // Better than to return path with possibly corrupt datastructure, which would likely cause crash
return s;
}
#endif // QT_NO_DATASTREAM
diff --git a/src/gui/painting/qpainterpath_p.h b/src/gui/painting/qpainterpath_p.h
index 4eb541ec65..22bdbde2a9 100644
--- a/src/gui/painting/qpainterpath_p.h
+++ b/src/gui/painting/qpainterpath_p.h
@@ -281,7 +281,7 @@ inline bool QPainterPathData::isClosed() const
inline void QPainterPathData::close()
{
- Q_ASSERT(ref.load() == 1);
+ Q_ASSERT(ref.loadRelaxed() == 1);
require_moveTo = true;
const QPainterPath::Element &first = elements.at(cStart);
QPainterPath::Element &last = elements.last();
@@ -308,7 +308,7 @@ inline void QPainterPathData::maybeMoveTo()
inline void QPainterPathData::clear()
{
- Q_ASSERT(ref.load() == 1);
+ Q_ASSERT(ref.loadRelaxed() == 1);
elements.clear();
diff --git a/src/gui/painting/qpen.cpp b/src/gui/painting/qpen.cpp
index 58a1716037..dc6e3e04d0 100644
--- a/src/gui/painting/qpen.cpp
+++ b/src/gui/painting/qpen.cpp
@@ -363,13 +363,13 @@ QPen::~QPen()
void QPen::detach()
{
- if (d->ref.load() == 1)
+ if (d->ref.loadRelaxed() == 1)
return;
QPenData *x = new QPenData(*static_cast<QPenData *>(d));
if (!d->ref.deref())
delete d;
- x->ref.store(1);
+ x->ref.storeRelaxed(1);
d = x;
}
@@ -885,7 +885,7 @@ bool QPen::operator==(const QPen &p) const
bool QPen::isDetached()
{
- return d->ref.load() == 1;
+ return d->ref.loadRelaxed() == 1;
}
diff --git a/src/gui/painting/qstroker.cpp b/src/gui/painting/qstroker.cpp
index 5b6990e667..271d3ba6bf 100644
--- a/src/gui/painting/qstroker.cpp
+++ b/src/gui/painting/qstroker.cpp
@@ -522,7 +522,7 @@ void QStroker::joinPoints(qfixed focal_x, qfixed focal_y, const QLineF &nextLine
QLineF shortCut(prevLine.p2(), nextLine.p1());
qreal angle = shortCut.angleTo(prevLine);
- if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) {
+ if ((type == QLineF::BoundedIntersection || (angle > qreal(90.01))) && nextLine.length() > offset) {
emitLineTo(focal_x, focal_y);
emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
return;
diff --git a/src/gui/painting/qtextureglyphcache.cpp b/src/gui/painting/qtextureglyphcache.cpp
index c21f2cdda4..e8c47df21c 100644
--- a/src/gui/painting/qtextureglyphcache.cpp
+++ b/src/gui/painting/qtextureglyphcache.cpp
@@ -285,6 +285,8 @@ QImageTextureGlyphCache::~QImageTextureGlyphCache()
void QImageTextureGlyphCache::resizeTextureData(int width, int height)
{
m_image = m_image.copy(0, 0, width, height);
+ // Regions not part of the copy are initialized to 0, and that is just what
+ // we need.
}
void QImageTextureGlyphCache::createTextureData(int width, int height)
@@ -305,6 +307,12 @@ void QImageTextureGlyphCache::createTextureData(int width, int height)
default:
Q_UNREACHABLE();
}
+
+ // Regions not touched by the glyphs must be initialized to 0. (such
+ // locations may in fact be sampled with styled (shifted) text materials)
+ // When resizing, the QImage copy() does this implicitly but the initial
+ // contents must be zeroed out explicitly here.
+ m_image.fill(0);
}
void QImageTextureGlyphCache::fillTexture(const Coord &c, glyph_t g, QFixed subPixelPosition)
diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp
new file mode 100644
index 0000000000..dbad63c6d1
--- /dev/null
+++ b/src/gui/rhi/qrhi.cpp
@@ -0,0 +1,5292 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qrhi_p_p.h"
+#include <qmath.h>
+
+#include "qrhinull_p_p.h"
+#ifndef QT_NO_OPENGL
+#include "qrhigles2_p_p.h"
+#endif
+#if QT_CONFIG(vulkan)
+#include "qrhivulkan_p_p.h"
+#endif
+#ifdef Q_OS_WIN
+#include "qrhid3d11_p_p.h"
+#endif
+//#ifdef Q_OS_DARWIN
+#ifdef Q_OS_MACOS
+#include "qrhimetal_p_p.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QRhi
+ \inmodule QtRhi
+
+ \brief Accelerated 2D/3D graphics API abstraction.
+
+ The Qt Rendering Hardware Interface is an abstraction for hardware accelerated
+ graphics APIs, such as, \l{https://www.khronos.org/opengl/}{OpenGL},
+ \l{https://www.khronos.org/opengles/}{OpenGL ES},
+ \l{https://docs.microsoft.com/en-us/windows/desktop/direct3d}{Direct3D},
+ \l{https://developer.apple.com/metal/}{Metal}, and
+ \l{https://www.khronos.org/vulkan/}{Vulkan}.
+
+ Some of the main design goals are:
+
+ \list
+
+ \li Simple, minimal, understandable, extensible. Follow the proven path of the
+ Qt Quick scenegraph.
+
+ \li Aim to be a product - and in the bigger picture, part of a product (Qt) -
+ that is usable out of the box both by internal (such as, Qt Quick) and,
+ eventually, external users.
+
+ \li Not a complete 1:1 wrapper for any of the underlying APIs. The feature set
+ is tuned towards the needs of Qt's 2D and 3D offering (QPainter, Qt Quick, Qt
+ 3D Studio). Iterate and evolve in a sustainable manner.
+
+ \li Intrinsically cross-platform, without reinventing: abstracting
+ cross-platform aspects of certain APIs (such as, OpenGL context creation and
+ windowing system interfaces, Vulkan instance and surface management) is not in
+ scope here. These are delegated to the existing QtGui facilities (QWindow,
+ QOpenGLContext, QVulkanInstance) and its backing QPA architecture.
+
+ \endlist
+
+ Each QRhi instance is backed by a backend for a specific graphics API. The
+ selection of the backend is a run time choice and is up to the application
+ or library that creates the QRhi instance. Some backends are available on
+ multiple platforms (OpenGL, Vulkan, Null), while APIs specific to a given
+ platform are only available when running on the platform in question (Metal
+ on macOS/iOS/tvOS, Direct3D on Windows).
+
+ The available backends currently are:
+
+ \list
+
+ \li OpenGL 2.1 or OpenGL ES 2.0 or newer. Some extensions are utilized when
+ present, for example to enable multisample framebuffers.
+
+ \li Direct3D 11.1
+
+ \li Metal
+
+ \li Vulkan 1.0, optionally with some extensions that are part of Vulkan 1.1
+
+ \li Null - A "dummy" backend that issues no graphics calls at all.
+
+ \endlist
+
+ In order to allow shader code to be written once in Qt applications and
+ libraries, all shaders are expected to be written in a single language
+ which is then compiled into SPIR-V. Versions for various shading language
+ are then generated from that, together with reflection information (inputs,
+ outputs, shader resources). This is then packed into easily and efficiently
+ serializable QShader instances. The compilers and tools to generate such
+ shaders are not part of QRhi, but the core classes for using such shaders,
+ QShader and QShaderDescription, are.
+
+ \section2 Design Fundamentals
+
+ A QRhi cannot be instantiated directly. Instead, use the create()
+ function. Delete the QRhi instance normally to release the graphics device.
+
+ \section3 Resources
+
+ Instances of classes deriving from QRhiResource, such as, QRhiBuffer,
+ QRhiTexture, etc., encapsulate zero, one, or more native graphics
+ resources. Instances of such classes are always created via the \c new
+ functions of the QRhi, such as, newBuffer(), newTexture(),
+ newTextureRenderTarget(), newSwapChain().
+
+ \badcode
+ vbuf = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData));
+ if (!vbuf->build()) { error }
+ ...
+ delete vbuf;
+ \endcode
+
+ \list
+
+ \li The returned value from both create() and functions like newBuffer() is
+ owned by the caller.
+
+ \li Just creating a QRhiResource subclass never allocates or initializes any
+ native resources. That is only done when calling the \c build function of a
+ subclass, for example, QRhiBuffer::build() or QRhiTexture::build().
+
+ \li The exception is
+ QRhiTextureRenderTarget::newCompatibleRenderPassDescriptor() and
+ QRhiSwapChain::newCompatibleRenderPassDescriptor(). There is no \c build
+ operation for these and the returned object is immediately active.
+
+ \li The resource objects themselves are treated as immutable: once a
+ resource is built, changing any parameters via the setters, such as,
+ QRhiTexture::setPixelSize(), has no effect, unless the underlying native
+ resource is released and \c build is called again. See more about resource
+ reuse in the sections below.
+
+ \li The underlying native resources are scheduled for releasing by the
+ QRhiResource destructor, or by calling QRhiResource::release(). Backends
+ often queue release requests and defer executing them to an unspecified
+ time, this is hidden from the applications. This way applications do not
+ have to worry about releasing native resources that may still be in use by
+ an in-flight frame.
+
+ \li Note that this does not mean that a QRhiResource can freely be
+ destroyed or release()'d within a frame (that is, in a
+ \l{QRhiCommandBuffer::beginFrame()}{beginFrame()} -
+ \l{QRhiCommandBuffer::endFrame()}{endFrame()} section). As a general rule,
+ all referenced QRhiResource objects must stay unchanged until the frame is
+ submitted by calling \l{QRhiCommandBuffer::endFrame()}{endFrame()}. To ease
+ this, QRhiResource::releaseAndDestroyLater() is provided as a convenience.
+
+ \endlist
+
+ \section3 Command buffers and deferred command execution
+
+ Regardless of the design and capabilities of the underlying graphics API,
+ all QRhi backends implement some level of command buffers. No
+ QRhiCommandBuffer function issues any native bind or draw command (such as,
+ \c glDrawElements) directly. Commands are always recorded in a queue,
+ either native or provided by the QRhi backend. The command buffer is
+ submitted, and so execution starts only upon QRhi::endFrame() or
+ QRhi::finish().
+
+ The deferred nature has consequences for some types of objects. For example,
+ writing to a dynamic buffer multiple times within a frame, in case such
+ buffers are backed by host-visible memory, will result in making the
+ results of all writes are visible to all draw calls in the command buffer
+ of the frame, regardless of when the dynamic buffer update was recorded
+ relative to a draw call.
+
+ Furthermore, instances of QRhiResource subclasses must be treated immutable
+ within a frame in which they are referenced in any way. Create or rebuild
+ all resources upfront, before starting to record commands for the next
+ frame. Reusing a QRhiResource instance within a frame (by rebuilding it and
+ then referencing it again in the same \c{beginFrame - endFrame} section)
+ should be avoided as it may lead to unexpected results, depending on the
+ backend.
+
+ As a general rule, all referenced QRhiResource objects must stay valid and
+ unmodified until the frame is submitted by calling
+ \l{QRhiCommandBuffer::endFrame()}{endFrame()}. On the other hand, calling
+ \l{QRhiResource::release()}{release()} or destroying the QRhiResource are
+ always safe once the frame is submitted, regardless of the status of the
+ underlying native resources (which may still be in use by the GPU - but
+ that is taken care of internally).
+
+ Unlike APIs like OpenGL, upload and copy type of commands cannot be mixed
+ with draw commands. The typical renderer will involve a sequence similar to
+ the following: \c{(re)build resources} - \c{begin frame} - \c{record
+ uploads and copies} - \c{start renderpass} - \c{record draw calls} - \c{end
+ renderpass} - \c{end frame}. Recording copy type of operations happens via
+ QRhiResourceUpdateBatch. Such operations are committed typically on
+ \l{QRhiCommandBuffer::beginPass()}{beginPass()}.
+
+ When working with legacy rendering engines designed for OpenGL, the
+ migration to QRhi often involves redesigning from having a single \c render
+ step (that performs copies and uploads, clears buffers, and issues draw
+ calls, all mixed together) to a clearly separated, two phase \c prepare -
+ \c render setup where the \c render step only starts a renderpass and
+ records draw calls, while all resource creation and queuing of updates,
+ uploads and copies happens beforehand, in the \c prepare step.
+
+ QRhi does not at the moment allow freely creating and submitting command
+ buffers. This may be lifted in the future to some extent, in particular if
+ compute support is introduced, but the model of well defined
+ \c{frame-start} and \c{frame-end} points, combined with a dedicated,
+ "frame" command buffer, where \c{frame-end} implies presenting, is going to
+ remain the primary way of operating since this is what fits Qt's various UI
+ technologies best.
+
+ \section3 Threading
+
+ A QRhi instance and the associated resources can be created and used on any
+ thread but all usage must be limited to that one single thread. When
+ rendering to multiple QWindows in an application, having a dedicated thread
+ and QRhi instance for each window is often advisable, as this can eliminate
+ issues with unexpected throttling caused by presenting to multiple windows.
+ Conceptually that is then the same as how Qt Quick scene graph's threaded
+ render loop operates when working directly with OpenGL: one thread for each
+ window, one QOpenGLContext for each thread. When moving onto QRhi,
+ QOpenGLContext is replaced by QRhi, making the migration straightforward.
+
+ When it comes to externally created native objects, such as OpenGL contexts
+ passed in via QRhiGles2NativeHandles, it is up to the application to ensure
+ they are not misused by other threads.
+
+ Resources are not shareable between QRhi instances. This is an intentional
+ choice since QRhi hides most queue, command buffer, and resource
+ synchronization related tasks, and provides no API for them. Safe and
+ efficient concurrent use of graphics resources from multiple threads is
+ tied to those concepts, however, and is thus a topic that is currently out
+ of scope, but may be introduced in the future.
+
+ \section3 Resource synchronization
+
+ QRhi does not expose APIs for resource barriers or image layout
+ transitions. Such synchronization is done implicitly by the backends, where
+ applicable (for example, Vulkan), by tracking resource usage as necessary.
+
+ \note Resources within a render or compute pass are expected to be bound to
+ a single usage during that pass. For example, a buffer can be used as
+ vertex, index, uniform, or storage buffer, but not a combination of them
+ within a single pass. However, it is perfectly fine to use a buffer as a
+ storage buffer in a compute pass, and then as a vertex buffer in a render
+ pass, for example, assuming the buffer declared both usages upon creation.
+
+ \note Textures have this rule relaxed in certain cases, because using two
+ subresources (typically two different mip levels) of the same texture for
+ different access (one for load, one for store) is supported even within the
+ same pass.
+
+ \section3 Resource reuse
+
+ From the user's point of view a QRhiResource is reusable immediately after
+ calling QRhiResource::release(). With the exception of swapchains, calling
+ \c build() on an already built object does an implicit \c release(). This
+ provides a handy shortcut to reuse a QRhiResource instance with different
+ parameters, with a new native graphics object underneath.
+
+ The importance of reusing the same object lies in the fact that some
+ objects reference other objects: for example, a QRhiShaderResourceBindings
+ can reference QRhiBuffer, QRhiTexture, and QRhiSampler instances. If in a
+ later frame one of these buffers need to be resized or a sampler parameter
+ needs changing, destroying and creating a whole new QRhiBuffer or
+ QRhiSampler would invalidate all references to the old instance. By just
+ changing the appropriate parameters via QRhiBuffer::setSize() or similar
+ and then calling QRhiBuffer::build(), everything works as expected and
+ there is no need to touch the QRhiShaderResourceBindings at all, even
+ though there is a good chance that under the hood the QRhiBuffer is now
+ backed by a whole new native buffer.
+
+ \badcode
+ ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 256);
+ ubuf->build();
+
+ srb = rhi->newShaderResourceBindings()
+ srb->setBindings({
+ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, ubuf)
+ });
+ srb->build();
+
+ ...
+
+ // now in a later frame we need to grow the buffer to a larger size
+ ubuf->setSize(512);
+ ubuf->build(); // same as ubuf->release(); ubuf->build();
+
+ // that's it, srb needs no changes whatsoever
+ \endcode
+
+ \section3 Pooled objects
+
+ In addition to resources, there are pooled objects as well, such as,
+ QRhiResourceUpdateBatch. An instance is retrieved via a \c next function,
+ such as, nextResourceUpdateBatch(). The caller does not own the returned
+ instance in this case. The only valid way of operating here is calling
+ functions on the QRhiResourceUpdateBatch and then passing it to
+ QRhiCommandBuffer::beginPass() or QRhiCommandBuffer::endPass(). These
+ functions take care of returning the batch to the pool. Alternatively, a
+ batch can be "canceled" and returned to the pool without processing by
+ calling QRhiResourceUpdateBatch::release().
+
+ A typical pattern is thus:
+
+ \badcode
+ QRhiResourceUpdateBatch *resUpdates = rhi->nextResourceUpdateBatch();
+ ...
+ resUpdates->updateDynamicBuffer(ubuf, 0, 64, mvp.constData());
+ if (!image.isNull()) {
+ resUpdates->uploadTexture(texture, image);
+ image = QImage();
+ }
+ ...
+ QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
+ cb->beginPass(swapchain->currentFrameRenderTarget(), clearCol, clearDs, resUpdates);
+ \endcode
+
+ \section3 Swapchain specifics
+
+ QRhiSwapChain features some special semantics due to the peculiar nature of
+ swapchains.
+
+ \list
+
+ \li It has no \c build but rather a QRhiSwapChain::buildOrResize().
+ Repeatedly calling this function is \b not the same as calling
+ QRhiSwapChain::release() followed by QRhiSwapChain::buildOrResize(). This
+ is because swapchains often have ways to handle the case where buffers need
+ to be resized in a manner that is more efficient than a brute force
+ destroying and recreating from scratch.
+
+ \li An active QRhiSwapChain must be released by calling
+ \l{QRhiSwapChain::release()}{release()}, or by destroying the object, before
+ the QWindow's underlying QPlatformWindow, and so the associated native
+ window object, is destroyed. It should not be postponed because releasing
+ the swapchain may become problematic (and with some APIs, like Vulkan, is
+ explicitly disallowed) when the native window is not around anymore, for
+ example because the QPlatformWindow got destroyed upon getting a
+ QWindow::close(). Therefore, releasing the swapchain must happen whenever
+ the targeted QWindow sends the
+ QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed event. If the event does
+ not arrive before the destruction of the QWindow - this can happen when
+ using QCoreApplication::quit() -, then check QWindow::handle() after the
+ event loop exits and invoke the swapchain release when non-null (meaning
+ the underlying native window is still around).
+
+ \endlist
+
+ \section3 Ownership
+
+ The general rule is no ownership transfer. Creating a QRhi with an already
+ existing graphics device does not mean the QRhi takes ownership of the
+ device object. Similarly, ownership is not given away when a device or
+ texture object is "exported" via QRhi::nativeHandles() or
+ QRhiTexture::nativeHandles(). Most importantly, passing pointers in structs
+ and via setters does not transfer ownership.
+ */
+
+/*!
+ \enum QRhi::Implementation
+ Describes which graphics API-specific backend gets used by a QRhi instance.
+
+ \value Null
+ \value Vulkan
+ \value OpenGLES2
+ \value D3D11
+ \value Metal
+ */
+
+/*!
+ \enum QRhi::Flag
+ Describes what special features to enable.
+
+ \value EnableProfiling Enables gathering timing (CPU, GPU) and resource
+ (QRhiBuffer, QRhiTexture, etc.) information and additional metadata. See
+ QRhiProfiler. Avoid enabling in production builds as it may involve a
+ performance penalty.
+
+ \value EnableDebugMarkers Enables debug marker groups. Without this frame
+ debugging features like making debug groups and custom resource name
+ visible in external GPU debugging tools will not be available and functions
+ like QRhiCommandBuffer::debugMarkBegin() will become a no-op. Avoid
+ enabling in production builds as it may involve a performance penalty.
+ */
+
+/*!
+ \enum QRhi::FrameOpResult
+ Describes the result of operations that can have a soft failure.
+
+ \value FrameOpSuccess Success
+
+ \value FrameOpError Unspecified error
+
+ \value FrameOpSwapChainOutOfDate The swapchain is in an inconsistent state
+ internally. This can be recoverable by attempting to repeat the operation
+ (such as, beginFrame()) later.
+
+ \value FrameOpDeviceLost The graphics device was lost. This can be
+ recoverable by attempting to repeat the operation (such as, beginFrame())
+ and releasing and reinitializing all objects backed by native graphics
+ resources.
+ */
+
+/*!
+ \enum QRhi::Feature
+ Flag values to indicate what features are supported by the backend currently in use.
+
+ \value MultisampleTexture Indicates that textures with a sample count larger
+ than 1 are supported.
+
+ \value MultisampleRenderBuffer Indicates that renderbuffers with a sample
+ count larger than 1 are supported.
+
+ \value DebugMarkers Indicates that debug marker groups (and so
+ QRhiCommandBuffer::debugMarkBegin()) are supported.
+
+ \value Timestamps Indicates that command buffer timestamps are supported.
+ Relevant for QRhiProfiler::gpuFrameTimes().
+
+ \value Instancing Indicates that instanced drawing is supported.
+
+ \value CustomInstanceStepRate Indicates that instance step rates other than
+ 1 are supported.
+
+ \value PrimitiveRestart Indicates that restarting the assembly of
+ primitives when encountering an index value of 0xFFFF
+ (\l{QRhiCommandBuffer::IndexUInt16}{IndexUInt16}) or 0xFFFFFFFF
+ (\l{QRhiCommandBuffer::IndexUInt32}{IndexUInt32}) is enabled, for certain
+ primitive topologies at least. QRhi will try to enable this with all
+ backends, but in some cases it will not be supported. Dynamically
+ controlling primitive restart is not possible since with some APIs
+ primitive restart with a fixed index is always on. Applications must assume
+ that whenever this feature is reported as supported, the above mentioned
+ index values \c may be treated specially, depending on the topology. The
+ only two topologies where primitive restart is guaranteed to behave
+ identically across backends, as long as this feature is reported as
+ supported, are \l{QRhiGraphicsPipeline::LineStrip}{LineStrip} and
+ \l{QRhiGraphicsPipeline::TriangleStrip}{TriangleStrip}.
+
+ \value NonDynamicUniformBuffers Indicates that creating buffers with the
+ usage \l{QRhiBuffer::UniformBuffer}{UniformBuffer} and the types
+ \l{QRhiBuffer::Immutable}{Immutable} or \l{QRhiBuffer::Static}{Static} is
+ supported. When reported as unsupported, uniform (constant) buffers must be
+ created as \l{QRhiBuffer::Dynamic}{Dynamic}. (which is recommended
+ regardless)
+
+ \value NonFourAlignedEffectiveIndexBufferOffset Indicates that effective
+ index buffer offsets (\c{indexOffset + firstIndex * indexComponentSize})
+ that are not 4 byte aligned are supported. When not supported, attempting
+ to issue a \l{QRhiCommandBuffer::drawIndexed()}{drawIndexed()} with a
+ non-aligned effective offset may lead to unspecified behavior.
+
+ \value NPOTTextureRepeat Indicates that the \l{QRhiSampler::Repeat}{Repeat}
+ mode is supported for textures with a non-power-of-two size.
+
+ \value RedOrAlpha8IsRed Indicates that the
+ \l{QRhiTexture::RED_OR_ALPHA8}{RED_OR_ALPHA8} format maps to a one
+ component 8-bit \c red format. This is the case for all backends except
+ OpenGL, where \c{GL_ALPHA}, a one component 8-bit \c alpha format, is used
+ instead. This is relevant for shader code that samples from the texture.
+
+ \value ElementIndexUint Indicates that 32-bit unsigned integer elements are
+ supported in the index buffer. In practice this is true everywhere except
+ when running on plain OpenGL ES 2.0 implementations without the necessary
+ extension. When false, only 16-bit unsigned elements are supported in the
+ index buffer.
+
+ \value Compute Indicates that compute shaders are supported.
+ */
+
+/*!
+ \enum QRhi::BeginFrameFlag
+ Flag values for QRhi::beginFrame()
+ */
+
+/*!
+ \enum QRhi::EndFrameFlag
+ Flag values for QRhi::endFrame()
+
+ \value SkipPresent Specifies that no present command is to be queued or no
+ swapBuffers call is to be made. This way no image is presented. Generating
+ multiple frames with all having this flag set is not recommended (except,
+ for example, for benchmarking purposes - but keep in mind that backends may
+ behave differently when it comes to waiting for command completion without
+ presenting so the results are not comparable between them)
+ */
+
+/*!
+ \enum QRhi::ResourceLimit
+ Describes the resource limit to query.
+
+ \value TextureSizeMin Minimum texture width and height. This is typically
+ 1. The minimum texture size is handled gracefully, meaning attempting to
+ create a texture with an empty size will instead create a texture with the
+ minimum size.
+
+ \value TextureSizeMax Maximum texture width and height. This depends on the
+ graphics API and sometimes the platform or implementation as well.
+ Typically the value is in the range 4096 - 16384. Attempting to create
+ textures larger than this is expected to fail.
+
+ \value MaxColorAttachments The maximum number of color attachments for a
+ QRhiTextureRenderTarget, in case multiple render targets are supported. When
+ MRT is not supported, the value is 1. Otherwise this is typically 8, but
+ watch out for the fact that OpenGL only mandates 4 as the minimum, and that
+ is what some OpenGL ES implementations provide.
+
+ \value FramesInFlight The number of frames the backend may keep "in
+ flight". The value has no relevance, and is unspecified, with backends like
+ OpenGL and Direct3D 11. With backends like Vulkan or Metal, it is the
+ responsibility of QRhi to block whenever starting a new frame and finding
+ the CPU is already \c{N - 1} frames ahead of the GPU (because the command
+ buffer submitted in frame no. \c{current} - \c{N} has not yet completed).
+ The value N is what is returned from here, and is typically 2. This can be
+ relevant to applications that integrate rendering done directly with the
+ graphics API, as such rendering code may want to perform double (if the
+ value is 2) buffering for resources, such as, buffers, similarly to the
+ QRhi backends themselves. The current frame slot index (a value running 0,
+ 1, .., N-1, then wrapping around) is retrievable from
+ QRhi::currentFrameSlot().
+ */
+
+/*!
+ \class QRhiInitParams
+ \inmodule QtRhi
+ \brief Base class for backend-specific initialization parameters.
+
+ Contains fields that are relevant to all backends.
+ */
+
+/*!
+ \class QRhiDepthStencilClearValue
+ \inmodule QtRhi
+ \brief Specifies clear values for a depth or stencil buffer.
+ */
+
+/*!
+ \fn QRhiDepthStencilClearValue::QRhiDepthStencilClearValue()
+
+ Constructs a depth/stencil clear value with depth clear value 1.0f and
+ stencil clear value 0.
+ */
+
+/*!
+ Constructs a depth/stencil clear value with depth clear value \a d and
+ stencil clear value \a s.
+ */
+QRhiDepthStencilClearValue::QRhiDepthStencilClearValue(float d, quint32 s)
+ : m_d(d),
+ m_s(s)
+{
+}
+
+/*!
+ \return \c true if the values in the two QRhiDepthStencilClearValue objects
+ \a a and \a b are equal.
+
+ \relates QRhiDepthStencilClearValue
+ */
+bool operator==(const QRhiDepthStencilClearValue &a, const QRhiDepthStencilClearValue &b) Q_DECL_NOTHROW
+{
+ return a.depthClearValue() == b.depthClearValue()
+ && a.stencilClearValue() == b.stencilClearValue();
+}
+
+/*!
+ \return \c false if the values in the two QRhiDepthStencilClearValue
+ objects \a a and \a b are equal; otherwise returns \c true.
+
+ \relates QRhiDepthStencilClearValue
+*/
+bool operator!=(const QRhiDepthStencilClearValue &a, const QRhiDepthStencilClearValue &b) Q_DECL_NOTHROW
+{
+ return !(a == b);
+}
+
+/*!
+ \return the hash value for \a v, using \a seed to seed the calculation.
+
+ \relates QRhiDepthStencilClearValue
+ */
+uint qHash(const QRhiDepthStencilClearValue &v, uint seed) Q_DECL_NOTHROW
+{
+ return seed * (qFloor(v.depthClearValue() * 100) + v.stencilClearValue());
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QRhiDepthStencilClearValue &v)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "QRhiDepthStencilClearValue(depth-clear=" << v.depthClearValue()
+ << " stencil-clear=" << v.stencilClearValue()
+ << ')';
+ return dbg;
+}
+#endif
+
+/*!
+ \class QRhiViewport
+ \inmodule QtRhi
+ \brief Specifies a viewport rectangle.
+
+ Used with QRhiCommandBuffer::setViewport().
+
+ \note QRhi assumes OpenGL-style viewport coordinates, meaning x and y are
+ bottom-left.
+
+ Typical usage is like the following:
+
+ \badcode
+ const QSize outputSizeInPixels = swapchain->currentPixelSize();
+ const QRhiViewport viewport(0, 0, outputSizeInPixels.width(), outputSizeInPixels.height());
+ cb->beginPass(swapchain->currentFrameRenderTarget(), { 0, 0, 0, 1 }, { 1, 0 });
+ cb->setGraphicsPipeline(ps);
+ cb->setViewport(viewport);
+ ...
+ \endcode
+
+ \sa QRhiCommandBuffer::setViewport(), QRhi::clipSpaceCorrMatrix(), QRhiScissor
+ */
+
+/*!
+ \fn QRhiViewport::QRhiViewport()
+
+ Constructs a viewport description with an empty rectangle and a depth range
+ of 0.0f - 1.0f.
+
+ \sa QRhi::clipSpaceCorrMatrix()
+ */
+
+/*!
+ Constructs a viewport description with the rectangle specified by \a x, \a
+ y, \a w, \a h and the depth range \a minDepth and \a maxDepth.
+
+ \note x and y are assumed to be the bottom-left position.
+
+ \sa QRhi::clipSpaceCorrMatrix()
+ */
+QRhiViewport::QRhiViewport(float x, float y, float w, float h, float minDepth, float maxDepth)
+ : m_rect { { x, y, w, h } },
+ m_minDepth(minDepth),
+ m_maxDepth(maxDepth)
+{
+}
+
+/*!
+ \return \c true if the values in the two QRhiViewport objects
+ \a a and \a b are equal.
+
+ \relates QRhiViewport
+ */
+bool operator==(const QRhiViewport &a, const QRhiViewport &b) Q_DECL_NOTHROW
+{
+ return a.viewport() == b.viewport()
+ && a.minDepth() == b.minDepth()
+ && a.maxDepth() == b.maxDepth();
+}
+
+/*!
+ \return \c false if the values in the two QRhiViewport
+ objects \a a and \a b are equal; otherwise returns \c true.
+
+ \relates QRhiViewport
+*/
+bool operator!=(const QRhiViewport &a, const QRhiViewport &b) Q_DECL_NOTHROW
+{
+ return !(a == b);
+}
+
+/*!
+ \return the hash value for \a v, using \a seed to seed the calculation.
+
+ \relates QRhiViewport
+ */
+uint qHash(const QRhiViewport &v, uint seed) Q_DECL_NOTHROW
+{
+ const std::array<float, 4> r = v.viewport();
+ return seed + r[0] + r[1] + r[2] + r[3] + qFloor(v.minDepth() * 100) + qFloor(v.maxDepth() * 100);
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QRhiViewport &v)
+{
+ QDebugStateSaver saver(dbg);
+ const std::array<float, 4> r = v.viewport();
+ dbg.nospace() << "QRhiViewport(bottom-left-x=" << r[0]
+ << " bottom-left-y=" << r[1]
+ << " width=" << r[2]
+ << " height=" << r[3]
+ << " minDepth=" << v.minDepth()
+ << " maxDepth=" << v.maxDepth()
+ << ')';
+ return dbg;
+}
+#endif
+
+/*!
+ \class QRhiScissor
+ \inmodule QtRhi
+ \brief Specifies a scissor rectangle.
+
+ Used with QRhiCommandBuffer::setScissor(). Setting a scissor rectangle is
+ only possible with a QRhiGraphicsPipeline that has
+ QRhiGraphicsPipeline::UsesScissor set.
+
+ \note QRhi assumes OpenGL-style scissor coordinates, meaning x and y are
+ bottom-left.
+
+ \sa QRhiCommandBuffer::setScissor(), QRhiViewport
+ */
+
+/*!
+ \fn QRhiScissor::QRhiScissor()
+
+ Constructs an empty scissor.
+ */
+
+/*!
+ Constructs a scissor with the rectangle specified by \a x, \a y, \a w, and
+ \a h.
+
+ \note x and y are assumed to be the bottom-left position.
+ */
+QRhiScissor::QRhiScissor(int x, int y, int w, int h)
+ : m_rect { { x, y, w, h } }
+{
+}
+
+/*!
+ \return \c true if the values in the two QRhiScissor objects
+ \a a and \a b are equal.
+
+ \relates QRhiScissor
+ */
+bool operator==(const QRhiScissor &a, const QRhiScissor &b) Q_DECL_NOTHROW
+{
+ return a.scissor() == b.scissor();
+}
+
+/*!
+ \return \c false if the values in the two QRhiScissor
+ objects \a a and \a b are equal; otherwise returns \c true.
+
+ \relates QRhiScissor
+*/
+bool operator!=(const QRhiScissor &a, const QRhiScissor &b) Q_DECL_NOTHROW
+{
+ return !(a == b);
+}
+
+/*!
+ \return the hash value for \a v, using \a seed to seed the calculation.
+
+ \relates QRhiScissor
+ */
+uint qHash(const QRhiScissor &v, uint seed) Q_DECL_NOTHROW
+{
+ const std::array<int, 4> r = v.scissor();
+ return seed + r[0] + r[1] + r[2] + r[3];
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QRhiScissor &s)
+{
+ QDebugStateSaver saver(dbg);
+ const std::array<int, 4> r = s.scissor();
+ dbg.nospace() << "QRhiScissor(bottom-left-x=" << r[0]
+ << " bottom-left-y=" << r[1]
+ << " width=" << r[2]
+ << " height=" << r[3]
+ << ')';
+ return dbg;
+}
+#endif
+
+/*!
+ \class QRhiVertexInputBinding
+ \inmodule QtRhi
+ \brief Describes a vertex input binding.
+
+ Specifies the stride (in bytes, must be a multiple of 4), the
+ classification and optionally the instance step rate.
+
+ As an example, assume a vertex shader with the following inputs:
+
+ \badcode
+ layout(location = 0) in vec4 position;
+ layout(location = 1) in vec2 texcoord;
+ \endcode
+
+ Now let's assume also that 3 component vertex positions \c{(x, y, z)} and 2
+ component texture coordinates \c{(u, v)} are provided in a non-interleaved
+ format in a buffer (or separate buffers even). Definining two bindings
+ could then be done like this:
+
+ \badcode
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({
+ { 3 * sizeof(float) },
+ { 2 * sizeof(float) }
+ });
+ \endcode
+
+ Only the stride is interesting here since instancing is not used. The
+ binding number is given by the index of the QRhiVertexInputBinding
+ element in the bindings vector of the QRhiVertexInputLayout.
+
+ Once a graphics pipeline with this vertex input layout is bound, the vertex
+ inputs could be set up like the following for drawing a cube with 36
+ vertices, assuming we have a single buffer with first the positions and
+ then the texture coordinates:
+
+ \badcode
+ const QRhiCommandBuffer::VertexInput vbufBindings[] = {
+ { cubeBuf, 0 },
+ { cubeBuf, 36 * 3 * sizeof(float) }
+ };
+ cb->setVertexInput(0, 2, vbufBindings);
+ \endcode
+
+ Note how the index defined by \c {startBinding + i}, where \c i is the
+ index in the second argument of
+ \l{QRhiCommandBuffer::setVertexInput()}{setVertexInput()}, matches the
+ index of the corresponding entry in the \c bindings vector of the
+ QRhiVertexInputLayout.
+
+ \note the stride must always be a multiple of 4.
+
+ \sa QRhiCommandBuffer::setVertexInput()
+ */
+
+/*!
+ \enum QRhiVertexInputBinding::Classification
+ Describes the input data classification.
+
+ \value PerVertex Data is per-vertex
+ \value PerInstance Data is per-instance
+ */
+
+/*!
+ \fn QRhiVertexInputBinding::QRhiVertexInputBinding()
+
+ Constructs a default vertex input binding description.
+ */
+
+/*!
+ Constructs a vertex input binding description with the specified \a stride,
+ classification \a cls, and instance step rate \a stepRate.
+
+ \note \a stepRate other than 1 is only supported when
+ QRhi::CustomInstanceStepRate is reported to be supported.
+ */
+QRhiVertexInputBinding::QRhiVertexInputBinding(quint32 stride, Classification cls, int stepRate)
+ : m_stride(stride),
+ m_classification(cls),
+ m_instanceStepRate(stepRate)
+{
+}
+
+/*!
+ \return \c true if the values in the two QRhiVertexInputBinding objects
+ \a a and \a b are equal.
+
+ \relates QRhiVertexInputBinding
+ */
+bool operator==(const QRhiVertexInputBinding &a, const QRhiVertexInputBinding &b) Q_DECL_NOTHROW
+{
+ return a.stride() == b.stride()
+ && a.classification() == b.classification()
+ && a.instanceStepRate() == b.instanceStepRate();
+}
+
+/*!
+ \return \c false if the values in the two QRhiVertexInputBinding
+ objects \a a and \a b are equal; otherwise returns \c true.
+
+ \relates QRhiVertexInputBinding
+*/
+bool operator!=(const QRhiVertexInputBinding &a, const QRhiVertexInputBinding &b) Q_DECL_NOTHROW
+{
+ return !(a == b);
+}
+
+/*!
+ \return the hash value for \a v, using \a seed to seed the calculation.
+
+ \relates QRhiVertexInputBinding
+ */
+uint qHash(const QRhiVertexInputBinding &v, uint seed) Q_DECL_NOTHROW
+{
+ return seed + v.stride() + v.classification();
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QRhiVertexInputBinding &b)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "QRhiVertexInputBinding(stride=" << b.stride()
+ << " cls=" << b.classification()
+ << " step-rate=" << b.instanceStepRate()
+ << ')';
+ return dbg;
+}
+#endif
+
+/*!
+ \class QRhiVertexInputAttribute
+ \inmodule QtRhi
+ \brief Describes a single vertex input element.
+
+ The members specify the binding number, location, format, and offset for a
+ single vertex input element.
+
+ \note For HLSL it is assumed that the vertex shader uses
+ \c{TEXCOORD<location>} as the semantic for each input. Hence no separate
+ semantic name and index.
+
+ As an example, assume a vertex shader with the following inputs:
+
+ \badcode
+ layout(location = 0) in vec4 position;
+ layout(location = 1) in vec2 texcoord;
+ \endcode
+
+ Now let's assume that we have 3 component vertex positions \c{(x, y, z)}
+ and 2 component texture coordinates \c{(u, v)} are provided in a
+ non-interleaved format in a buffer (or separate buffers even). Once two
+ bindings are defined, the attributes could be specified as:
+
+ \badcode
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({
+ { 3 * sizeof(float) },
+ { 2 * sizeof(float) }
+ });
+ inputLayout.setAttributes({
+ { 0, 0, QRhiVertexInputAttribute::Float3, 0 },
+ { 1, 1, QRhiVertexInputAttribute::Float2, 0 }
+ });
+ \endcode
+
+ Once a graphics pipeline with this vertex input layout is bound, the vertex
+ inputs could be set up like the following for drawing a cube with 36
+ vertices, assuming we have a single buffer with first the positions and
+ then the texture coordinates:
+
+ \badcode
+ const QRhiCommandBuffer::VertexInput vbufBindings[] = {
+ { cubeBuf, 0 },
+ { cubeBuf, 36 * 3 * sizeof(float) }
+ };
+ cb->setVertexInput(0, 2, vbufBindings);
+ \endcode
+
+ When working with interleaved data, there will typically be just one
+ binding, with multiple attributes referring to that same buffer binding
+ point:
+
+ \badcode
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({
+ { 5 * sizeof(float) }
+ });
+ inputLayout.setAttributes({
+ { 0, 0, QRhiVertexInputAttribute::Float3, 0 },
+ { 0, 1, QRhiVertexInputAttribute::Float2, 3 * sizeof(float) }
+ });
+ \endcode
+
+ and then:
+
+ \badcode
+ const QRhiCommandBuffer::VertexInput vbufBinding(interleavedCubeBuf, 0);
+ cb->setVertexInput(0, 1, &vbufBinding);
+ \endcode
+
+ \sa QRhiCommandBuffer::setVertexInput()
+ */
+
+/*!
+ \enum QRhiVertexInputAttribute::Format
+ Specifies the type of the element data.
+
+ \value Float4 Four component float vector
+ \value Float3 Three component float vector
+ \value Float2 Two component float vector
+ \value Float Float
+ \value UNormByte4 Four component normalized unsigned byte vector
+ \value UNormByte2 Two component normalized unsigned byte vector
+ \value UNormByte Normalized unsigned byte
+ */
+
+/*!
+ \fn QRhiVertexInputAttribute::QRhiVertexInputAttribute()
+
+ Constructs a default vertex input attribute description.
+ */
+
+/*!
+ Constructs a vertex input attribute description with the specified \a
+ binding number, \a location, \a format, and \a offset.
+ */
+QRhiVertexInputAttribute::QRhiVertexInputAttribute(int binding, int location, Format format, quint32 offset)
+ : m_binding(binding),
+ m_location(location),
+ m_format(format),
+ m_offset(offset)
+{
+}
+
+/*!
+ \return \c true if the values in the two QRhiVertexInputAttribute objects
+ \a a and \a b are equal.
+
+ \relates QRhiVertexInputAttribute
+ */
+bool operator==(const QRhiVertexInputAttribute &a, const QRhiVertexInputAttribute &b) Q_DECL_NOTHROW
+{
+ return a.binding() == b.binding()
+ && a.location() == b.location()
+ && a.format() == b.format()
+ && a.offset() == b.offset();
+}
+
+/*!
+ \return \c false if the values in the two QRhiVertexInputAttribute
+ objects \a a and \a b are equal; otherwise returns \c true.
+
+ \relates QRhiVertexInputAttribute
+*/
+bool operator!=(const QRhiVertexInputAttribute &a, const QRhiVertexInputAttribute &b) Q_DECL_NOTHROW
+{
+ return !(a == b);
+}
+
+/*!
+ \return the hash value for \a v, using \a seed to seed the calculation.
+
+ \relates QRhiVertexInputAttribute
+ */
+uint qHash(const QRhiVertexInputAttribute &v, uint seed) Q_DECL_NOTHROW
+{
+ return seed + v.binding() + v.location() + v.format() + v.offset();
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QRhiVertexInputAttribute &a)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "QRhiVertexInputAttribute(binding=" << a.binding()
+ << " location=" << a.location()
+ << " format=" << a.format()
+ << " offset=" << a.offset()
+ << ')';
+ return dbg;
+}
+#endif
+
+/*!
+ \class QRhiVertexInputLayout
+ \inmodule QtRhi
+ \brief Describes the layout of vertex inputs consumed by a vertex shader.
+
+ The vertex input layout is defined by the collections of
+ QRhiVertexInputBinding and QRhiVertexInputAttribute.
+ */
+
+/*!
+ \fn QRhiVertexInputLayout::QRhiVertexInputLayout()
+
+ Constructs an empty vertex input layout description.
+ */
+
+/*!
+ \return \c true if the values in the two QRhiVertexInputLayout objects
+ \a a and \a b are equal.
+
+ \relates QRhiVertexInputLayout
+ */
+bool operator==(const QRhiVertexInputLayout &a, const QRhiVertexInputLayout &b) Q_DECL_NOTHROW
+{
+ return a.bindings() == b.bindings()
+ && a.attributes() == b.attributes();
+}
+
+/*!
+ \return \c false if the values in the two QRhiVertexInputLayout
+ objects \a a and \a b are equal; otherwise returns \c true.
+
+ \relates QRhiVertexInputLayout
+*/
+bool operator!=(const QRhiVertexInputLayout &a, const QRhiVertexInputLayout &b) Q_DECL_NOTHROW
+{
+ return !(a == b);
+}
+
+/*!
+ \return the hash value for \a v, using \a seed to seed the calculation.
+
+ \relates QRhiVertexInputLayout
+ */
+uint qHash(const QRhiVertexInputLayout &v, uint seed) Q_DECL_NOTHROW
+{
+ return qHash(v.bindings(), seed) + qHash(v.attributes(), seed);
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QRhiVertexInputLayout &v)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "QRhiVertexInputLayout(bindings=" << v.bindings()
+ << " attributes=" << v.attributes()
+ << ')';
+ return dbg;
+}
+#endif
+
+/*!
+ \class QRhiShaderStage
+ \inmodule QtRhi
+ \brief Specifies the type and the shader code for a shader stage in the pipeline.
+ */
+
+/*!
+ \enum QRhiShaderStage::Type
+ Specifies the type of the shader stage.
+
+ \value Vertex Vertex stage
+ \value Fragment Fragment (pixel) stage
+ \value Compute Compute stage (this may not always be supported at run time)
+ */
+
+/*!
+ \fn QRhiShaderStage::QRhiShaderStage()
+
+ Constructs a shader stage description for the vertex stage with an empty
+ QShader.
+ */
+
+/*!
+ Constructs a shader stage description with the \a type of the stage and the
+ \a shader.
+
+ The shader variant \a v defaults to QShader::StandardShader. A
+ QShader contains multiple source and binary versions of a shader.
+ In addition, it can also contain variants of the shader with slightly
+ modified code. \a v can then be used to select the desired variant.
+ */
+QRhiShaderStage::QRhiShaderStage(Type type, const QShader &shader, QShader::Variant v)
+ : m_type(type),
+ m_shader(shader),
+ m_shaderVariant(v)
+{
+}
+
+/*!
+ \return \c true if the values in the two QRhiShaderStage objects
+ \a a and \a b are equal.
+
+ \relates QRhiShaderStage
+ */
+bool operator==(const QRhiShaderStage &a, const QRhiShaderStage &b) Q_DECL_NOTHROW
+{
+ return a.type() == b.type()
+ && a.shader() == b.shader()
+ && a.shaderVariant() == b.shaderVariant();
+}
+
+/*!
+ \return \c false if the values in the two QRhiShaderStage
+ objects \a a and \a b are equal; otherwise returns \c true.
+
+ \relates QRhiShaderStage
+*/
+bool operator!=(const QRhiShaderStage &a, const QRhiShaderStage &b) Q_DECL_NOTHROW
+{
+ return !(a == b);
+}
+
+/*!
+ \return the hash value for \a v, using \a seed to seed the calculation.
+
+ \relates QRhiShaderStage
+ */
+uint qHash(const QRhiShaderStage &v, uint seed) Q_DECL_NOTHROW
+{
+ return v.type() + qHash(v.shader(), seed) + v.shaderVariant();
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QRhiShaderStage &s)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "QRhiShaderStage(type=" << s.type()
+ << " shader=" << s.shader()
+ << " variant=" << s.shaderVariant()
+ << ')';
+ return dbg;
+}
+#endif
+
+/*!
+ \class QRhiColorAttachment
+ \inmodule QtRhi
+ \brief Describes the a single color attachment of a render target.
+
+ A color attachment is either a QRhiTexture or a QRhiRenderBuffer. The
+ former, when texture() is set, is used in most cases.
+
+ \note texture() and renderBuffer() cannot be both set (be non-null at the
+ same time).
+
+ Setting renderBuffer instead is recommended only when multisampling is
+ needed. Relying on QRhi::MultisampleRenderBuffer is a better choice than
+ QRhi::MultisampleTexture in practice since the former is available in more
+ run time configurations (e.g. when running on OpenGL ES 3.0 which has no
+ support for multisample textures, but does support multisample
+ renderbuffers).
+
+ When targeting a non-multisample texture, the layer() and level()
+ indicate the targeted layer (face index \c{0-5} for cubemaps) and mip
+ level.
+
+ When texture() or renderBuffer() is multisample, resolveTexture() can be
+ set optionally. When set, samples are resolved automatically into that
+ (non-multisample) texture at the end of the render pass. When rendering
+ into a multisample renderbuffers, this is the only way to get resolved,
+ non-multisample content out of them. Multisample textures allow sampling in
+ shaders so for them this is just one option.
+
+ \note when resolving is enabled, the multisample data may not be written
+ out at all. This means that the multisample texture() must not be used
+ afterwards with shaders for sampling when resolveTexture() is set.
+ */
+
+/*!
+ \fn QRhiColorAttachment::QRhiColorAttachment()
+
+ Constructs an empty color attachment description.
+ */
+
+/*!
+ Constructs a color attachment description that specifies \a texture as the
+ associated color buffer.
+ */
+QRhiColorAttachment::QRhiColorAttachment(QRhiTexture *texture)
+ : m_texture(texture)
+{
+}
+
+/*!
+ Constructs a color attachment description that specifies \a renderBuffer as
+ the associated color buffer.
+ */
+QRhiColorAttachment::QRhiColorAttachment(QRhiRenderBuffer *renderBuffer)
+ : m_renderBuffer(renderBuffer)
+{
+}
+
+/*!
+ \class QRhiTextureRenderTargetDescription
+ \inmodule QtRhi
+ \brief Describes the color and depth or depth/stencil attachments of a render target.
+
+ A texture render target has zero or more textures as color attachments,
+ zero or one renderbuffer as combined depth/stencil buffer or zero or one
+ texture as depth buffer.
+
+ \note depthStencilBuffer() and depthTexture() cannot be both set (cannot be
+ non-null at the same time).
+ */
+
+/*!
+ \fn QRhiTextureRenderTargetDescription::QRhiTextureRenderTargetDescription()
+
+ Constructs an empty texture render target description.
+ */
+
+/*!
+ Constructs a texture render target description with one attachment
+ described by \a colorAttachment.
+ */
+QRhiTextureRenderTargetDescription::QRhiTextureRenderTargetDescription(const QRhiColorAttachment &colorAttachment)
+{
+ m_colorAttachments.append(colorAttachment);
+}
+
+/*!
+ Constructs a texture render target description with two attachments, a
+ color attachment described by \a colorAttachment, and a depth/stencil
+ attachment with \a depthStencilBuffer.
+ */
+QRhiTextureRenderTargetDescription::QRhiTextureRenderTargetDescription(const QRhiColorAttachment &colorAttachment,
+ QRhiRenderBuffer *depthStencilBuffer)
+ : m_depthStencilBuffer(depthStencilBuffer)
+{
+ m_colorAttachments.append(colorAttachment);
+}
+
+/*!
+ Constructs a texture render target description with two attachments, a
+ color attachment described by \a colorAttachment, and a depth attachment
+ with \a depthTexture.
+
+ \note \a depthTexture must have a suitable format, such as QRhiTexture::D16
+ or QRhiTexture::D32F.
+ */
+QRhiTextureRenderTargetDescription::QRhiTextureRenderTargetDescription(const QRhiColorAttachment &colorAttachment,
+ QRhiTexture *depthTexture)
+ : m_depthTexture(depthTexture)
+{
+ m_colorAttachments.append(colorAttachment);
+}
+
+/*!
+ \class QRhiTextureSubresourceUploadDescription
+ \inmodule QtRhi
+ \brief Describes the source for one mip level in a layer in a texture upload operation.
+
+ The source content is specified either as a QImage or as a raw blob. The
+ former is only allowed for uncompressed textures with a format that can be
+ mapped to QImage, while the latter is supported for all formats, including
+ floating point and compressed.
+
+ \note image() and data() cannot be both set at the same time.
+
+ destinationTopLeft() specifies the top-left corner of the target
+ rectangle. Defaults to (0, 0).
+
+ An empty sourceSize() (the default) indicates that size is assumed to be
+ the size of the subresource. With QImage-based uploads this implies that
+ the size of the source image() must match the subresource. When providing
+ raw data instead, sufficient number of bytes must be provided in data().
+
+ \note With compressed textures the first upload must always match the
+ subresource size due to graphics API limitations with some backends.
+
+ sourceTopLeft() is supported only for QImage-based uploads, and specifies
+ the top-left corner of the source rectangle.
+
+ \note Setting sourceSize() or sourceTopLeft() may trigger a QImage copy
+ internally, depending on the format and the backend.
+
+ When providing raw data, the stride (row pitch, row length in bytes) of the
+ provided data must be equal to \c{width * pixelSize} where \c pixelSize is
+ the number of bytes used for one pixel, and there must be no additional
+ padding between rows. There is no row start alignment requirement.
+
+ \note The format of the source data must be compatible with the texture
+ format. With many graphics APIs the data is copied as-is into a staging
+ buffer, there is no intermediate format conversion provided by QRhi. This
+ applies to floating point formats as well, with, for example, RGBA16F
+ requiring half floats in the source data.
+ */
+
+/*!
+ \fn QRhiTextureSubresourceUploadDescription::QRhiTextureSubresourceUploadDescription()
+
+ Constructs an empty subresource description.
+
+ \note an empty QRhiTextureSubresourceUploadDescription is not useful on its
+ own and should not be submitted to a QRhiTextureUploadEntry. At minimum
+ image or data must be set first.
+ */
+
+/*!
+ Constructs a mip level description with a \a image.
+
+ The \l{QImage::size()}{size} of \a image must match the size of the mip
+ level. For level 0 that is the \l{QRhiTexture::pixelSize()}{texture size}.
+
+ The bit depth of \a image must be compatible with the
+ \l{QRhiTexture::Format}{texture format}.
+
+ To describe a partial upload, call setSourceSize(), setSourceTopLeft(), or
+ setDestinationTopLeft() afterwards.
+ */
+QRhiTextureSubresourceUploadDescription::QRhiTextureSubresourceUploadDescription(const QImage &image)
+ : m_image(image)
+{
+}
+
+/*!
+ Constructs a mip level description with the image data is specified by \a
+ data and \a size. This is suitable for floating point and compressed
+ formats as well.
+
+ \a data can safely be destroyed or changed once this function returns.
+ */
+QRhiTextureSubresourceUploadDescription::QRhiTextureSubresourceUploadDescription(const void *data, int size)
+ : m_data(reinterpret_cast<const char *>(data), size)
+{
+}
+
+/*!
+ \class QRhiTextureUploadEntry
+ \inmodule QtRhi
+ \brief Describes one layer (face for cubemaps) in a texture upload operation.
+ */
+
+/*!
+ \fn QRhiTextureUploadEntry::QRhiTextureUploadEntry()
+
+ Constructs an empty QRhiTextureUploadEntry targeting layer 0 and level 0.
+
+ \note an empty QRhiTextureUploadEntry should not be submitted without
+ setting a QRhiTextureSubresourceUploadDescription via setDescription()
+ first.
+ */
+
+/*!
+ Constructs a QRhiTextureUploadEntry targeting the given \a layer and mip
+ \a level, with the subresource contents described by \a desc.
+ */
+QRhiTextureUploadEntry::QRhiTextureUploadEntry(int layer, int level,
+ const QRhiTextureSubresourceUploadDescription &desc)
+ : m_layer(layer),
+ m_level(level),
+ m_desc(desc)
+{
+}
+
+/*!
+ \class QRhiTextureUploadDescription
+ \inmodule QtRhi
+ \brief Describes a texture upload operation.
+
+ Used with QRhiResourceUpdateBatch::uploadTexture(). That function has two
+ variants: one taking a QImage and one taking a
+ QRhiTextureUploadDescription. The former is a convenience version,
+ internally creating a QRhiTextureUploadDescription with a single image
+ targeting level 0 for layer 0. However, when cubemaps, pre-generated mip
+ images, or compressed textures are involved, applications will have to work
+ directly with this class instead.
+
+ QRhiTextureUploadDescription also enables specifying batched uploads, which
+ are useful for example when generating an atlas or glyph cache texture:
+ multiple, partial uploads for the same subresource (meaning the same layer
+ and level) are supported, and can be, depending on the backend and the
+ underlying graphics API, more efficient when batched into the same
+ QRhiTextureUploadDescription as opposed to issuing individual
+ \l{QRhiResourceUpdateBatch::uploadTexture()}{uploadTexture()} commands for
+ each of them.
+
+ \note Cubemaps have one layer for each of the six faces in the order +X,
+ -X, +Y, -Y, +Z, -Z.
+
+ For example, specifying the faces of a cubemap could look like the following:
+
+ \badcode
+ QImage faces[6];
+ ...
+ QVector<QRhiTextureUploadEntry> entries;
+ for (int i = 0; i < 6; ++i)
+ entries.append(QRhiTextureUploadEntry(i, 0, faces[i]));
+ QRhiTextureUploadDescription desc(entries);
+ resourceUpdates->uploadTexture(texture, desc);
+ \endcode
+
+ Another example that specifies mip images for a compressed texture:
+
+ \badcode
+ QRhiTextureUploadDescription desc;
+ const int mipCount = rhi->mipLevelsForSize(compressedTexture->pixelSize());
+ for (int level = 0; level < mipCount; ++level) {
+ const QByteArray compressedDataForLevel = ..
+ desc.append(QRhiTextureUploadEntry(0, level, compressedDataForLevel));
+ }
+ resourceUpdates->uploadTexture(compressedTexture, desc);
+ \endcode
+
+ With partial uploads targeting the same subresource, it is recommended to
+ batch them into a single upload request, whenever possible:
+
+ \badcode
+ QRhiTextureSubresourceUploadDescription subresDesc(image);
+ subresDesc.setSourceSize(QSize(10, 10));
+ subResDesc.setDestinationTopLeft(QPoint(50, 40));
+ QRhiTextureUploadEntry entry(0, 0, subresDesc); // layer 0, level 0
+
+ QRhiTextureSubresourceUploadDescription subresDesc2(image);
+ subresDesc2.setSourceSize(QSize(30, 40));
+ subResDesc2.setDestinationTopLeft(QPoint(100, 200));
+ QRhiTextureUploadEntry entry2(0, 0, subresDesc2); // layer 0, level 0, i.e. same subresource
+
+ QRhiTextureUploadDescription desc({ entry, entry2});
+ resourceUpdates->uploadTexture(texture, desc);
+ \endcode
+ */
+
+/*!
+ \fn QRhiTextureUploadDescription::QRhiTextureUploadDescription()
+
+ Constructs an empty texture upload description.
+ */
+
+/*!
+ Constructs a texture upload description with a single subresource upload
+ described by \a entry.
+ */
+QRhiTextureUploadDescription::QRhiTextureUploadDescription(const QRhiTextureUploadEntry &entry)
+{
+ m_entries.append(entry);
+}
+
+/*!
+ Constructs a texture upload description with the specified list of \a entries.
+
+ \note \a entries can also contain multiple QRhiTextureUploadEntry elements
+ with the the same layer and level. This makes sense when those uploads are
+ partial, meaning their subresource description has a source size or image
+ smaller than the subresource dimensions, and can be more efficient than
+ issuing separate uploadTexture()'s.
+ */
+QRhiTextureUploadDescription::QRhiTextureUploadDescription(const QVector<QRhiTextureUploadEntry> &entries)
+ : m_entries(entries)
+{
+}
+
+/*!
+ Adds \a entry to the list of subresource uploads.
+ */
+void QRhiTextureUploadDescription::append(const QRhiTextureUploadEntry &entry)
+{
+ m_entries.append(entry);
+}
+
+/*!
+ \class QRhiTextureCopyDescription
+ \inmodule QtRhi
+ \brief Describes a texture-to-texture copy operation.
+
+ An empty pixelSize() indicates that the entire subresource is to be copied.
+ A default constructed copy description therefore leads to copying the
+ entire subresource at level 0 of layer 0.
+
+ \note The source texture must be created with
+ QRhiTexture::UsedAsTransferSource.
+
+ \note The source and destination rectangles defined by pixelSize(),
+ sourceTopLeft(), and destinationTopLeft() must fit the source and
+ destination textures, respectively. The behavior is undefined otherwise.
+ */
+
+/*!
+ \fn QRhiTextureCopyDescription::QRhiTextureCopyDescription()
+
+ Constructs an empty texture copy description.
+ */
+
+/*!
+ \class QRhiReadbackDescription
+ \inmodule QtRhi
+ \brief Describes a readback (reading back texture contents from possibly GPU-only memory) operation.
+
+ The source of the readback operation is either a QRhiTexture or the
+ current backbuffer of the currently targeted QRhiSwapChain. When
+ texture() is not set, the swapchain is used. Otherwise the specified
+ QRhiTexture is treated as the source.
+
+ \note Textures used in readbacks must be created with
+ QRhiTexture::UsedAsTransferSource.
+
+ \note Swapchains used in readbacks must be created with
+ QRhiSwapChain::UsedAsTransferSource.
+
+ layer() and level() are only applicable when the source is a QRhiTexture.
+
+ \note Multisample textures cannot be read back. Readbacks are supported for
+ multisample swapchain buffers however.
+ */
+
+/*!
+ \fn QRhiReadbackDescription::QRhiReadbackDescription()
+
+ Constructs an empty texture readback description.
+
+ \note The source texture is set to null by default, which is still a valid
+ readback: it specifies that the backbuffer of the current swapchain is to
+ be read back. (current meaning the frame's target swapchain at the time of
+ committing the QRhiResourceUpdateBatch with the
+ \l{QRhiResourceUpdateBatch::readBackTexture()}{texture readback} on it)
+ */
+
+/*!
+ Constructs an texture readback description that specifies that level 0 of
+ layer 0 of \a texture is to be read back.
+
+ \note \a texture can also be null in which case this constructor is
+ identical to the argumentless variant.
+ */
+QRhiReadbackDescription::QRhiReadbackDescription(QRhiTexture *texture)
+ : m_texture(texture)
+{
+}
+
+/*!
+ \class QRhiReadbackResult
+ \inmodule QtRhi
+ \brief Describes the results of a potentially asynchronous readback operation.
+
+ When \l completed is set, the function is invoked when the \l data is
+ available. \l format and \l pixelSize are set upon completion together with
+ \l data.
+ */
+
+/*!
+ \class QRhiNativeHandles
+ \inmodule QtRhi
+ \brief Base class for classes exposing backend-specific collections of native resource objects.
+ */
+
+/*!
+ \class QRhiResource
+ \inmodule QtRhi
+ \brief Base class for classes encapsulating native resource objects.
+ */
+
+/*!
+ \fn QRhiResource::Type QRhiResource::resourceType() const
+
+ \return the type of the resource.
+ */
+
+/*!
+ \internal
+ */
+QRhiResource::QRhiResource(QRhiImplementation *rhi)
+ : m_rhi(rhi)
+{
+ m_id = QRhiGlobalObjectIdGenerator::newId();
+}
+
+/*!
+ Destructor.
+
+ Releases (or requests deferred releasing of) the underlying native graphics
+ resources, if there are any.
+
+ \note Resources referenced by commands for the current frame should not be
+ released until the frame is submitted by QRhi::endFrame().
+
+ \sa release()
+ */
+QRhiResource::~QRhiResource()
+{
+ // release() cannot be called here, it being virtual; it is up to the
+ // subclasses to do that.
+}
+
+/*!
+ \fn void QRhiResource::release()
+
+ Releases (or requests deferred releasing of) the underlying native graphics
+ resources. Safe to call multiple times, subsequent invocations will be a
+ no-op then.
+
+ Once release() is called, the QRhiResource instance can be reused, by
+ calling \c build() again. That will then result in creating new native
+ graphics resources underneath.
+
+ \note Resources referenced by commands for the current frame should not be
+ released until the frame is submitted by QRhi::endFrame().
+
+ The QRhiResource destructor also performs the same task, so calling this
+ function is not necessary before destroying a QRhiResource.
+
+ \sa releaseAndDestroyLater()
+ */
+
+/*!
+ When called without a frame being recorded, this function is equivalent to
+ deleting the object. Between a QRhi::beginFrame() and QRhi::endFrame()
+ however the behavior is different: the QRhiResource will not be destroyed
+ until the frame is submitted via QRhi::endFrame(), thus satisfying the QRhi
+ requirement of not altering QRhiResource objects that are referenced by the
+ frame being recorded.
+
+ \sa release()
+ */
+void QRhiResource::releaseAndDestroyLater()
+{
+ m_rhi->addReleaseAndDestroyLater(this);
+}
+
+/*!
+ \return the currently set object name. By default the name is empty.
+ */
+QByteArray QRhiResource::name() const
+{
+ return m_objectName;
+}
+
+/*!
+ Sets a \a name for the object.
+
+ This has two uses: to get descriptive names for the native graphics
+ resources visible in graphics debugging tools, such as
+ \l{https://renderdoc.org/}{RenderDoc} and
+ \l{https://developer.apple.com/xcode/}{XCode}, and in the output stream of
+ QRhiProfiler.
+
+ When it comes to naming native objects by relaying the name via the
+ appropriate graphics API, note that the name is ignored when
+ QRhi::DebugMarkers are not supported, and may, depending on the backend,
+ also be ignored when QRhi::EnableDebugMarkers is not set.
+
+ \note The name may be ignored for objects other than buffers,
+ renderbuffers, and textures, depending on the backend.
+
+ \note The name may be modified. For slotted resources, such as a QRhiBuffer
+ backed by multiple native buffers, QRhi will append a suffix to make the
+ underlying native buffers easily distinguishable from each other.
+ */
+void QRhiResource::setName(const QByteArray &name)
+{
+ m_objectName = name;
+ m_objectName.replace(',', '_'); // cannot contain comma for QRhiProfiler
+}
+
+/*!
+ \return the global, unique identifier of this QRhiResource.
+
+ User code rarely needs to deal with the value directly. It is used
+ internally for tracking and bookkeeping purposes.
+ */
+quint64 QRhiResource::globalResourceId() const
+{
+ return m_id;
+}
+
+/*!
+ \class QRhiBuffer
+ \inmodule QtRhi
+ \brief Vertex, index, or uniform (constant) buffer resource.
+ */
+
+/*!
+ \enum QRhiBuffer::Type
+ Specifies storage type of buffer resource.
+
+ \value Immutable Indicates that the data is not expected to change ever
+ after the initial upload. Under the hood such buffer resources are
+ typically placed in device local (GPU) memory (on systems where
+ applicable). Uploading new data is possible, but may be expensive. The
+ upload typically happens by copying to a separate, host visible staging
+ buffer from which a GPU buffer-to-buffer copy is issued into the actual
+ GPU-only buffer.
+
+ \value Static Indicates that the data is expected to change only
+ infrequently. Typically placed in device local (GPU) memory, where
+ applicable. On backends where host visible staging buffers are used for
+ uploading, the staging buffers are kept around for this type, unlike with
+ Immutable, so subsequent uploads do not suffer in performance. Frequent
+ updates, especially updates in consecutive frames, should be avoided.
+
+ \value Dynamic Indicates that the data is expected to change frequently.
+ Not recommended for large buffers. Typically backed by host visible memory
+ in 2 copies in order to allow for changing without stalling the graphics
+ pipeline. The double buffering is managed transparently to the applications
+ and is not exposed in the API here in any form. This is the recommended,
+ and, with some backends, the only possible, type for buffers with
+ UniformBuffer usage.
+ */
+
+/*!
+ \enum QRhiBuffer::UsageFlag
+ Flag values to specify how the buffer is going to be used.
+
+ \value VertexBuffer Vertex buffer. This allows the QRhiBuffer to be used in
+ \l{setVertexInput()}{QRhiCommandBuffer::setVertexInput()}.
+
+ \value IndexBuffer Index buffer. This allows the QRhiBuffer to be used in
+ \l{setVertexInput()}{QRhiCommandBuffer::setVertexInput()}.
+
+ \value UniformBuffer Uniform buffer (also called constant buffer). This
+ allows the QRhiBuffer to be used in combination with
+ \l{UniformBuffer}{QRhiShaderResourceBinding::UniformBuffer}. When
+ \l{QRhi::NonDynamicUniformBuffers}{NonDynamicUniformBuffers} is reported as
+ not supported, this usage can only be combined with the type Dynamic.
+
+ \value StorageBuffer Storage buffer. This allows the QRhiBuffer to be used
+ in combination with \l{BufferLoad}{QRhiShaderResourceBinding::BufferLoad},
+ \l{BufferStore}{QRhiShaderResourceBinding::BufferStore}, or
+ \l{BufferLoadStore}{QRhiShaderResourceBinding::BufferLoadStore}. This usage
+ can only be combined with the types Immutable or Static, and is only
+ available when the \l{QRhi::Compute}{Compute feature} is reported as
+ supported.
+ */
+
+/*!
+ \fn void QRhiBuffer::setSize(int sz)
+
+ Sets the size of the buffer in bytes. The size is normally specified in
+ QRhi::newBuffer() so this function is only used when the size has to be
+ changed. As with other setters, the size only takes effect when calling
+ build(), and for already built buffers this involves releasing the previous
+ native resource and creating new ones under the hood.
+
+ Backends may choose to allocate buffers bigger than \a sz in order to
+ fulfill alignment requirements. This is hidden from the applications and
+ size() will always report the size requested in \a sz.
+ */
+
+/*!
+ \internal
+ */
+QRhiBuffer::QRhiBuffer(QRhiImplementation *rhi, Type type_, UsageFlags usage_, int size_)
+ : QRhiResource(rhi),
+ m_type(type_), m_usage(usage_), m_size(size_)
+{
+}
+
+/*!
+ \return the resource type.
+ */
+QRhiResource::Type QRhiBuffer::resourceType() const
+{
+ return Buffer;
+}
+
+/*!
+ \fn bool QRhiBuffer::build()
+
+ Creates the corresponding native graphics resources. If there are already
+ resources present due to an earlier build() with no corresponding
+ release(), then release() is called implicitly first.
+
+ \return \c true when successful, \c false when a graphics operation failed.
+ Regardless of the return value, calling release() is always safe.
+ */
+
+/*!
+ \class QRhiRenderBuffer
+ \inmodule QtRhi
+ \brief Renderbuffer resource.
+
+ Renderbuffers cannot be sampled or read but have some benefits over
+ textures in some cases:
+
+ A DepthStencil renderbuffer may be lazily allocated and be backed by
+ transient memory with some APIs. On some platforms this may mean the
+ depth/stencil buffer uses no physical backing at all.
+
+ Color renderbuffers are useful since QRhi::MultisampleRenderBuffer may be
+ supported even when QRhi::MultisampleTexture is not.
+
+ How the renderbuffer is implemented by a backend is not exposed to the
+ applications. In some cases it may be backed by ordinary textures, while in
+ others there may be a different kind of native resource used.
+ */
+
+/*!
+ \enum QRhiRenderBuffer::Type
+ Specifies the type of the renderbuffer
+
+ \value DepthStencil Combined depth/stencil
+ \value Color Color
+ */
+
+/*!
+ \enum QRhiRenderBuffer::Flag
+ Flag values for flags() and setFlags()
+
+ \value UsedWithSwapChainOnly For DepthStencil renderbuffers this indicates
+ that the renderbuffer is only used in combination with a QRhiSwapChain and
+ never in other ways. Relevant with some backends, while others ignore it.
+ With OpenGL where a separate windowing system interface API is in use (EGL,
+ GLX, etc.), the flag is important since it avoids creating any actual
+ resource as there is already a windowing system provided depth/stencil
+ buffer as requested by QSurfaceFormat.
+ */
+
+/*!
+ \internal
+ */
+QRhiRenderBuffer::QRhiRenderBuffer(QRhiImplementation *rhi, Type type_, const QSize &pixelSize_,
+ int sampleCount_, Flags flags_)
+ : QRhiResource(rhi),
+ m_type(type_), m_pixelSize(pixelSize_), m_sampleCount(sampleCount_), m_flags(flags_)
+{
+}
+
+/*!
+ \return the resource type.
+ */
+QRhiResource::Type QRhiRenderBuffer::resourceType() const
+{
+ return RenderBuffer;
+}
+
+/*!
+ \fn bool QRhiRenderBuffer::build()
+
+ Creates the corresponding native graphics resources. If there are already
+ resources present due to an earlier build() with no corresponding
+ release(), then release() is called implicitly first.
+
+ \return \c true when successful, \c false when a graphics operation failed.
+ Regardless of the return value, calling release() is always safe.
+ */
+
+/*!
+ \fn QRhiTexture::Format QRhiRenderBuffer::backingFormat() const
+
+ \internal
+ */
+
+/*!
+ \class QRhiTexture
+ \inmodule QtRhi
+ \brief Texture resource.
+ */
+
+/*!
+ \enum QRhiTexture::Flag
+
+ Flag values to specify how the texture is going to be used. Not honoring
+ the flags set before build() and attempting to use the texture in ways that
+ was not declared upfront can lead to unspecified behavior or decreased
+ performance depending on the backend and the underlying graphics API.
+
+ \value RenderTarget The texture going to be used in combination with
+ QRhiTextureRenderTarget.
+
+ \value CubeMap The texture is a cubemap. Such textures have 6 layers, one
+ for each face in the order of +X, -X, +Y, -Y, +Z, -Z. Cubemap textures
+ cannot be multisample.
+
+ \value MipMapped The texture has mipmaps. The appropriate mip count is
+ calculated automatically and can also be retrieved via
+ QRhi::mipLevelsForSize(). The images for the mip levels have to be
+ provided in the texture uploaded or generated via
+ QRhiResourceUpdateBatch::generateMips(). Multisample textures cannot have
+ mipmaps.
+
+ \value sRGB Use an sRGB format.
+
+ \value UsedAsTransferSource The texture is used as the source of a texture
+ copy or readback, meaning the texture is given as the source in
+ QRhiResourceUpdateBatch::copyTexture() or
+ QRhiResourceUpdateBatch::readBackTexture().
+
+ \value UsedWithGenerateMips The texture is going to be used with
+ QRhiResourceUpdateBatch::generateMips().
+
+ \value UsedWithLoadStore The texture is going to be used with image
+ load/store operations, for example, in a compute shader.
+ */
+
+/*!
+ \enum QRhiTexture::Format
+
+ Specifies the texture format. See also QRhi::isTextureFormatSupported() and
+ note that flags() can modify the format when QRhiTexture::sRGB is set.
+
+ \value UnknownFormat Not a valid format. This cannot be passed to setFormat().
+
+ \value RGBA8 Four component, unsigned normalized 8 bit per component. Always supported.
+
+ \value BGRA8 Four component, unsigned normalized 8 bit per component.
+
+ \value R8 One component, unsigned normalized 8 bit.
+
+ \value R16 One component, unsigned normalized 16 bit.
+
+ \value RED_OR_ALPHA8 Either same as R8, or is a similar format with the component swizzled to alpha,
+ depending on \l{QRhi::RedOrAlpha8IsRed}{RedOrAlpha8IsRed}.
+
+ \value RGBA16F Four components, 16-bit float per component.
+
+ \value RGBA32F Four components, 32-bit float per component.
+
+ \value D16 16-bit depth (normalized unsigned integer)
+
+ \value D32F 32-bit depth (32-bit float)
+
+ \value BC1
+ \value BC2
+ \value BC3
+ \value BC4
+ \value BC5
+ \value BC6H
+ \value BC7
+
+ \value ETC2_RGB8
+ \value ETC2_RGB8A1
+ \value ETC2_RGBA8
+
+ \value ASTC_4x4
+ \value ASTC_5x4
+ \value ASTC_5x5
+ \value ASTC_6x5
+ \value ASTC_6x6
+ \value ASTC_8x5
+ \value ASTC_8x6
+ \value ASTC_8x8
+ \value ASTC_10x5
+ \value ASTC_10x6
+ \value ASTC_10x8
+ \value ASTC_10x10
+ \value ASTC_12x10
+ \value ASTC_12x12
+ */
+
+/*!
+ \internal
+ */
+QRhiTexture::QRhiTexture(QRhiImplementation *rhi, Format format_, const QSize &pixelSize_,
+ int sampleCount_, Flags flags_)
+ : QRhiResource(rhi),
+ m_format(format_), m_pixelSize(pixelSize_), m_sampleCount(sampleCount_), m_flags(flags_)
+{
+}
+
+/*!
+ \return the resource type.
+ */
+QRhiResource::Type QRhiTexture::resourceType() const
+{
+ return Texture;
+}
+
+/*!
+ \fn bool QRhiTexture::build()
+
+ Creates the corresponding native graphics resources. If there are already
+ resources present due to an earlier build() with no corresponding
+ release(), then release() is called implicitly first.
+
+ \return \c true when successful, \c false when a graphics operation failed.
+ Regardless of the return value, calling release() is always safe.
+ */
+
+/*!
+ \return a pointer to a backend-specific QRhiNativeHandles subclass, such as
+ QRhiVulkanTextureNativeHandles. The returned value is null when exposing
+ the underlying native resources is not supported by the backend.
+
+ \sa QRhiVulkanTextureNativeHandles, QRhiD3D11TextureNativeHandles,
+ QRhiMetalTextureNativeHandles, QRhiGles2TextureNativeHandles
+ */
+const QRhiNativeHandles *QRhiTexture::nativeHandles()
+{
+ return nullptr;
+}
+
+/*!
+ Similar to build() except that no new native textures are created. Instead,
+ the texture from \a src is used.
+
+ This allows importing an existing native texture object (which must belong
+ to the same device or sharing context, depending on the graphics API) from
+ an external graphics engine.
+
+ \note format(), pixelSize(), sampleCount(), and flags() must still be set
+ correctly. Passing incorrect sizes and other values to QRhi::newTexture()
+ and then following it with a buildFrom() expecting that the native texture
+ object alone is sufficient to deduce such values is \b wrong and will lead
+ to problems.
+
+ \note QRhiTexture does not take ownership of the texture object. release()
+ does not free the object or any associated memory.
+
+ The opposite of this operation, exposing a QRhiTexture-created native
+ texture object to a foreign engine, is possible via nativeHandles().
+
+ \sa QRhiVulkanTextureNativeHandles, QRhiD3D11TextureNativeHandles,
+ QRhiMetalTextureNativeHandles, QRhiGles2TextureNativeHandles
+ */
+bool QRhiTexture::buildFrom(const QRhiNativeHandles *src)
+{
+ Q_UNUSED(src);
+ return false;
+}
+
+/*!
+ \class QRhiSampler
+ \inmodule QtRhi
+ \brief Sampler resource.
+ */
+
+/*!
+ \enum QRhiSampler::Filter
+ Specifies the minification, magnification, or mipmap filtering
+
+ \value None Applicable only for mipmapMode(), indicates no mipmaps to be used
+ \value Nearest
+ \value Linear
+ */
+
+/*!
+ \enum QRhiSampler::AddressMode
+ Specifies the addressing mode
+
+ \value Repeat
+ \value ClampToEdge
+ \value Border
+ \value Mirror
+ \value MirrorOnce
+ */
+
+/*!
+ \enum QRhiSampler::CompareOp
+ Specifies the texture comparison function.
+
+ \value Never (default)
+ \value Less
+ \value Equal
+ \value LessOrEqual
+ \value Greater
+ \value NotEqual
+ \value GreaterOrEqual
+ \value Always
+ */
+
+/*!
+ \internal
+ */
+QRhiSampler::QRhiSampler(QRhiImplementation *rhi,
+ Filter magFilter_, Filter minFilter_, Filter mipmapMode_,
+ AddressMode u_, AddressMode v_)
+ : QRhiResource(rhi),
+ m_magFilter(magFilter_), m_minFilter(minFilter_), m_mipmapMode(mipmapMode_),
+ m_addressU(u_), m_addressV(v_),
+ m_addressW(QRhiSampler::ClampToEdge),
+ m_compareOp(QRhiSampler::Never)
+{
+}
+
+/*!
+ \return the resource type.
+ */
+QRhiResource::Type QRhiSampler::resourceType() const
+{
+ return Sampler;
+}
+
+/*!
+ \class QRhiRenderPassDescriptor
+ \inmodule QtRhi
+ \brief Render pass resource.
+ */
+
+/*!
+ \internal
+ */
+QRhiRenderPassDescriptor::QRhiRenderPassDescriptor(QRhiImplementation *rhi)
+ : QRhiResource(rhi)
+{
+}
+
+/*!
+ \return the resource type.
+ */
+QRhiResource::Type QRhiRenderPassDescriptor::resourceType() const
+{
+ return RenderPassDescriptor;
+}
+
+/*!
+ \class QRhiRenderTarget
+ \inmodule QtRhi
+ \brief Represents an onscreen (swapchain) or offscreen (texture) render target.
+ */
+
+/*!
+ \internal
+ */
+QRhiRenderTarget::QRhiRenderTarget(QRhiImplementation *rhi)
+ : QRhiResource(rhi)
+{
+}
+
+/*!
+ \return the resource type.
+ */
+QRhiResource::Type QRhiRenderTarget::resourceType() const
+{
+ return RenderTarget;
+}
+
+/*!
+ \fn QSize QRhiRenderTarget::pixelSize() const
+
+ \return the size in pixels.
+ */
+
+/*!
+ \fn float QRhiRenderTarget::devicePixelRatio() const
+
+ \return the device pixel ratio. For QRhiTextureRenderTarget this is always
+ 1. For targets retrieved from a QRhiSwapChain the value reflects the
+ \l{QWindow::devicePixelRatio()}{device pixel ratio} of the targeted
+ QWindow.
+ */
+
+/*!
+ \class QRhiTextureRenderTarget
+ \inmodule QtRhi
+ \brief Texture render target resource.
+
+ A texture render target allows rendering into one or more textures,
+ optionally with a depth texture or depth/stencil renderbuffer.
+
+ \note Textures used in combination with QRhiTextureRenderTarget must be
+ created with the QRhiTexture::RenderTarget flag.
+
+ The simplest example of creating a render target with a texture as its
+ single color attachment:
+
+ \badcode
+ texture = rhi->newTexture(QRhiTexture::RGBA8, size, 1, QRhiTexture::RenderTarget);
+ texture->build();
+ rt = rhi->newTextureRenderTarget({ texture });
+ rp = rt->newCompatibleRenderPassDescriptor();
+ rt->setRenderPassDescriptor(rt);
+ rt->build();
+ // rt can now be used with beginPass()
+ \endcode
+ */
+
+/*!
+ \enum QRhiTextureRenderTarget::Flag
+
+ Flag values describing the load/store behavior for the render target. The
+ load/store behavior may be baked into native resources under the hood,
+ depending on the backend, and therefore it needs to be known upfront and
+ cannot be changed without rebuilding (and so releasing and creating new
+ native resources).
+
+ \value PreserveColorContents Indicates that the contents of the color
+ attachments is to be loaded when starting a render pass, instead of
+ clearing. This is potentially more expensive, especially on mobile (tiled)
+ GPUs, but allows preserving the existing contents between passes.
+
+ \value PreserveDepthStencilContents Indicates that the contents of the
+ depth texture is to be loaded when starting a render pass, instead
+ clearing. Only applicable when a texture is used as the depth buffer
+ (QRhiTextureRenderTargetDescription::depthTexture() is set) because
+ depth/stencil renderbuffers may not have any physical backing and data may
+ not be written out in the first place.
+ */
+
+/*!
+ \internal
+ */
+QRhiTextureRenderTarget::QRhiTextureRenderTarget(QRhiImplementation *rhi,
+ const QRhiTextureRenderTargetDescription &desc_,
+ Flags flags_)
+ : QRhiRenderTarget(rhi),
+ m_desc(desc_),
+ m_flags(flags_)
+{
+}
+
+/*!
+ \return the resource type.
+ */
+QRhiResource::Type QRhiTextureRenderTarget::resourceType() const
+{
+ return TextureRenderTarget;
+}
+
+/*!
+ \fn QRhiRenderPassDescriptor *QRhiTextureRenderTarget::newCompatibleRenderPassDescriptor()
+
+ \return a new QRhiRenderPassDescriptor that is compatible with this render
+ target.
+
+ The returned value is used in two ways: it can be passed to
+ setRenderPassDescriptor() and
+ QRhiGraphicsPipeline::setRenderPassDescriptor(). A render pass descriptor
+ describes the attachments (color, depth/stencil) and the load/store
+ behavior that can be affected by flags(). A QRhiGraphicsPipeline can only
+ be used in combination with a render target that has the same
+ QRhiRenderPassDescriptor set.
+
+ Two QRhiTextureRenderTarget instances can share the same render pass
+ descriptor as long as they have the same number and type of attachments.
+ The associated QRhiTexture or QRhiRenderBuffer instances are not part of
+ the render pass descriptor so those can differ in the two
+ QRhiTextureRenderTarget intances.
+
+ \note resources, such as QRhiTexture instances, referenced in description()
+ must already be built
+
+ \sa build()
+ */
+
+/*!
+ \fn bool QRhiTextureRenderTarget::build()
+
+ Creates the corresponding native graphics resources. If there are already
+ resources present due to an earlier build() with no corresponding
+ release(), then release() is called implicitly first.
+
+ \note renderPassDescriptor() must be set before calling build(). To obtain
+ a QRhiRenderPassDescriptor compatible with the render target, call
+ newCompatibleRenderPassDescriptor() before build() but after setting all
+ other parameters, such as description() and flags(). To save resources,
+ reuse the same QRhiRenderPassDescriptor with multiple
+ QRhiTextureRenderTarget instances, whenever possible. Sharing the same
+ render pass descriptor is only possible when the render targets have the
+ same number and type of attachments (the actual textures can differ) and
+ the same flags.
+
+ \note resources, such as QRhiTexture instances, referenced in description()
+ must already be built
+
+ \return \c true when successful, \c false when a graphics operation failed.
+ Regardless of the return value, calling release() is always safe.
+ */
+
+/*!
+ \class QRhiShaderResourceBindings
+ \inmodule QtRhi
+ \brief Encapsulates resources for making buffer, texture, sampler resources visible to shaders.
+
+ A QRhiShaderResourceBindings is a collection of QRhiShaderResourceBinding
+ objects, each of which describe a single binding.
+
+ Take a fragment shader with the following interface:
+
+ \badcode
+ layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+ int flip;
+ } ubuf;
+
+ layout(binding = 1) uniform sampler2D tex;
+ \endcode
+
+ To make resources visible to the shader, the following
+ QRhiShaderResourceBindings could be created and then passed to
+ QRhiGraphicsPipeline::setShaderResourceBindings():
+
+ \badcode
+ srb = rhi->newShaderResourceBindings();
+ srb->setBindings({
+ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, ubuf),
+ QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, texture, sampler)
+ });
+ srb->build();
+ ...
+ ps = rhi->newGraphicsPipeline();
+ ...
+ ps->setShaderResourceBindings(srb);
+ ps->build();
+ ...
+ cb->setGraphicsPipeline(ps);
+ cb->setShaderResources(); // binds srb
+ \endcode
+
+ This assumes that \c ubuf is a QRhiBuffer, \c texture is a QRhiTexture,
+ while \a sampler is a QRhiSampler. The example also assumes that the
+ uniform block is present in the vertex shader as well so the same buffer is
+ made visible to the vertex stage too.
+
+ \section3 Advanced usage
+
+ Building on the above example, let's assume that a pass now needs to use
+ the exact same pipeline and shaders with a different texture. Creating a
+ whole separate QRhiGraphicsPipeline just for this would be an overkill.
+ This is why QRhiCommandBuffer::setShaderResources() allows specifying a \a
+ srb argument. As long as the layouts (so the number of bindings and the
+ binding points) match between two QRhiShaderResourceBindings, they can both
+ be used with the same pipeline, assuming the pipeline was built with one of
+ them in the first place.
+
+ Creating and then using a new \c srb2 that is very similar to \c srb with
+ the exception of referencing another texture could be implemented like the
+ following:
+
+ \badcode
+ srb2 = rhi->newShaderResourceBindings();
+ QVector<QRhiShaderResourceBinding> bindings = srb->bindings();
+ bindings[1] = QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, anotherTexture, sampler);
+ srb2->setBindings(bindings);
+ srb2->build();
+ ...
+ cb->setGraphicsPipeline(ps);
+ cb->setShaderResources(srb2); // binds srb2
+ \endcode
+ */
+
+/*!
+ \internal
+ */
+QRhiShaderResourceBindings::QRhiShaderResourceBindings(QRhiImplementation *rhi)
+ : QRhiResource(rhi)
+{
+}
+
+/*!
+ \return the resource type.
+ */
+QRhiResource::Type QRhiShaderResourceBindings::resourceType() const
+{
+ return ShaderResourceBindings;
+}
+
+/*!
+ \return \c true if the layout is compatible with \a other. The layout does
+ not include the actual resource (such as, buffer or texture) and related
+ parameters (such as, offset or size). It does include the binding point,
+ pipeline stage, and resource type, however. The number and order of the
+ bindings must also match in order to be compatible.
+
+ When there is a QRhiGraphicsPipeline created with this
+ QRhiShaderResourceBindings, and the function returns \c true, \a other can
+ then safely be passed to QRhiCommandBuffer::setShaderResources(), and so
+ be used with the pipeline in place of this QRhiShaderResourceBindings.
+
+ This function can be called before build() as well. The bindings must
+ already be set via setBindings() however.
+ */
+bool QRhiShaderResourceBindings::isLayoutCompatible(const QRhiShaderResourceBindings *other) const
+{
+ const int count = m_bindings.count();
+ if (count != other->m_bindings.count())
+ return false;
+
+ for (int i = 0; i < count; ++i) {
+ if (!m_bindings[i].isLayoutCompatible(other->m_bindings.at(i)))
+ return false;
+ }
+
+ return true;
+}
+
+/*!
+ \class QRhiShaderResourceBinding
+ \inmodule QtRhi
+ \brief Describes the shader resource for a single binding point.
+
+ A QRhiShaderResourceBinding cannot be constructed directly. Instead, use
+ the static functions uniformBuffer(), sampledTexture() to get an instance.
+ */
+
+/*!
+ \enum QRhiShaderResourceBinding::Type
+ Specifies type of the shader resource bound to a binding point
+
+ \value UniformBuffer Uniform buffer
+
+ \value SampledTexture Combined image sampler
+
+ \value ImageLoad Image load (with GLSL this maps to doing imageLoad() on a
+ single level - and either one or all layers - of a texture exposed to the
+ shader as an image object)
+
+ \value ImageStore Image store (with GLSL this maps to doing imageStore() or
+ imageAtomic*() on a single level - and either one or all layers - of a
+ texture exposed to the shader as an image object)
+
+ \value ImageLoadStore Image load and store
+
+ \value BufferLoad Storage buffer store (with GLSL this maps to reading from
+ a shader storage buffer)
+
+ \value BufferStore Storage buffer store (with GLSL this maps to writing to
+ a shader storage buffer)
+
+ \value BufferLoadStore Storage buffer load and store
+ */
+
+/*!
+ \enum QRhiShaderResourceBinding::StageFlag
+ Flag values to indicate which stages the shader resource is visible in
+
+ \value VertexStage Vertex stage
+ \value FragmentStage Fragment (pixel) stage
+ \value ComputeStage Compute stage
+ */
+
+/*!
+ \internal
+ */
+QRhiShaderResourceBinding::QRhiShaderResourceBinding()
+ : d(new QRhiShaderResourceBindingPrivate)
+{
+}
+
+/*!
+ \internal
+ */
+void QRhiShaderResourceBinding::detach()
+{
+ qAtomicDetach(d);
+}
+
+/*!
+ \internal
+ */
+QRhiShaderResourceBinding::QRhiShaderResourceBinding(const QRhiShaderResourceBinding &other)
+ : d(other.d)
+{
+ d->ref.ref();
+}
+
+/*!
+ \internal
+ */
+QRhiShaderResourceBinding &QRhiShaderResourceBinding::operator=(const QRhiShaderResourceBinding &other)
+{
+ qAtomicAssign(d, other.d);
+ return *this;
+}
+
+/*!
+ Destructor.
+ */
+QRhiShaderResourceBinding::~QRhiShaderResourceBinding()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+/*!
+ \return \c true if the layout is compatible with \a other. The layout does not
+ include the actual resource (such as, buffer or texture) and related
+ parameters (such as, offset or size).
+
+ For example, \c a and \c b below are not equal, but are compatible layout-wise:
+
+ \badcode
+ auto a = QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, buffer);
+ auto b = QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, someOtherBuffer, 256);
+ \endcode
+ */
+bool QRhiShaderResourceBinding::isLayoutCompatible(const QRhiShaderResourceBinding &other) const
+{
+ return (d == other.d)
+ || (d->binding == other.d->binding && d->stage == other.d->stage && d->type == other.d->type);
+}
+
+/*!
+ \return a shader resource binding for the given binding number, pipeline
+ stages, and buffer specified by \a binding, \a stage, and \a buf.
+
+ \note \a buf must have been created with QRhiBuffer::UniformBuffer.
+ */
+QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBuffer(
+ int binding, StageFlags stage, QRhiBuffer *buf)
+{
+ QRhiShaderResourceBinding b;
+ QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b);
+ Q_ASSERT(d->ref.load() == 1);
+ d->binding = binding;
+ d->stage = stage;
+ d->type = UniformBuffer;
+ d->u.ubuf.buf = buf;
+ d->u.ubuf.offset = 0;
+ d->u.ubuf.maybeSize = 0; // entire buffer
+ d->u.ubuf.hasDynamicOffset = false;
+ return b;
+}
+
+/*!
+ \return a shader resource binding for the given binding number, pipeline
+ stages, and buffer specified by \a binding, \a stage, and \a buf. This
+ overload binds a region only, as specified by \a offset and \a size.
+
+ \note It is up to the user to ensure the offset is aligned to
+ QRhi::ubufAlignment().
+
+ \note \a size must be greater than 0.
+
+ \note \a buf must have been created with QRhiBuffer::UniformBuffer.
+ */
+QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBuffer(
+ int binding, StageFlags stage, QRhiBuffer *buf, int offset, int size)
+{
+ Q_ASSERT(size > 0);
+ QRhiShaderResourceBinding b = uniformBuffer(binding, stage, buf);
+ QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b);
+ d->u.ubuf.offset = offset;
+ d->u.ubuf.maybeSize = size;
+ return b;
+}
+
+/*!
+ \return a shader resource binding for the given binding number, pipeline
+ stages, and buffer specified by \a binding, \a stage, and \a buf. The
+ uniform buffer is assumed to have dynamic offset. The dynamic offset can be
+ specified in QRhiCommandBuffer::setShaderResources(), thus allowing using
+ varying offset values without creating new bindings for the buffer. The
+ size of the bound region is specified by \a size. Like with non-dynamic
+ offsets, \c{offset + size} cannot exceed the size of \a buf.
+
+ \note \a buf must have been created with QRhiBuffer::UniformBuffer.
+ */
+QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(
+ int binding, StageFlags stage, QRhiBuffer *buf, int size)
+{
+ QRhiShaderResourceBinding b = uniformBuffer(binding, stage, buf, 0, size);
+ QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b);
+ d->u.ubuf.hasDynamicOffset = true;
+ return b;
+}
+
+/*!
+ \return a shader resource binding for the given binding number, pipeline
+ stages, texture, and sampler specified by \a binding, \a stage, \a tex,
+ \a sampler.
+ */
+QRhiShaderResourceBinding QRhiShaderResourceBinding::sampledTexture(
+ int binding, StageFlags stage, QRhiTexture *tex, QRhiSampler *sampler)
+{
+ QRhiShaderResourceBinding b;
+ QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b);
+ Q_ASSERT(d->ref.load() == 1);
+ d->binding = binding;
+ d->stage = stage;
+ d->type = SampledTexture;
+ d->u.stex.tex = tex;
+ d->u.stex.sampler = sampler;
+ return b;
+}
+
+/*!
+ \return a shader resource binding for a read-only storage image with the
+ given \a binding number and pipeline \a stage. The image load operations
+ will have access to all layers of the specified \a level. (so if the texture
+ is a cubemap, the shader must use imageCube instead of image2D)
+
+ \note \a tex must have been created with QRhiTexture::UsedWithLoadStore.
+ */
+QRhiShaderResourceBinding QRhiShaderResourceBinding::imageLoad(
+ int binding, StageFlags stage, QRhiTexture *tex, int level)
+{
+ QRhiShaderResourceBinding b;
+ QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b);
+ Q_ASSERT(d->ref.load() == 1);
+ d->binding = binding;
+ d->stage = stage;
+ d->type = ImageLoad;
+ d->u.simage.tex = tex;
+ d->u.simage.level = level;
+ return b;
+}
+
+/*!
+ \return a shader resource binding for a write-only storage image with the
+ given \a binding number and pipeline \a stage. The image store operations
+ will have access to all layers of the specified \a level. (so if the texture
+ is a cubemap, the shader must use imageCube instead of image2D)
+
+ \note \a tex must have been created with QRhiTexture::UsedWithLoadStore.
+ */
+QRhiShaderResourceBinding QRhiShaderResourceBinding::imageStore(
+ int binding, StageFlags stage, QRhiTexture *tex, int level)
+{
+ QRhiShaderResourceBinding b = imageLoad(binding, stage, tex, level);
+ QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b);
+ d->type = ImageStore;
+ return b;
+}
+
+/*!
+ \return a shader resource binding for a read/write storage image with the
+ given \a binding number and pipeline \a stage. The image load/store operations
+ will have access to all layers of the specified \a level. (so if the texture
+ is a cubemap, the shader must use imageCube instead of image2D)
+
+ \note \a tex must have been created with QRhiTexture::UsedWithLoadStore.
+ */
+QRhiShaderResourceBinding QRhiShaderResourceBinding::imageLoadStore(
+ int binding, StageFlags stage, QRhiTexture *tex, int level)
+{
+ QRhiShaderResourceBinding b = imageLoad(binding, stage, tex, level);
+ QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b);
+ d->type = ImageLoadStore;
+ return b;
+}
+
+/*!
+ \return a shader resource binding for a read-only storage buffer with the
+ given \a binding number and pipeline \a stage.
+
+ \note \a buf must have been created with QRhiBuffer::StorageBuffer.
+ */
+QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferLoad(
+ int binding, StageFlags stage, QRhiBuffer *buf)
+{
+ QRhiShaderResourceBinding b;
+ QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b);
+ Q_ASSERT(d->ref.load() == 1);
+ d->binding = binding;
+ d->stage = stage;
+ d->type = BufferLoad;
+ d->u.sbuf.buf = buf;
+ d->u.sbuf.offset = 0;
+ d->u.sbuf.maybeSize = 0; // entire buffer
+ return b;
+}
+
+/*!
+ \return a shader resource binding for a read-only storage buffer with the
+ given \a binding number and pipeline \a stage. This overload binds a region
+ only, as specified by \a offset and \a size.
+
+ \note \a buf must have been created with QRhiBuffer::StorageBuffer.
+ */
+QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferLoad(
+ int binding, StageFlags stage, QRhiBuffer *buf, int offset, int size)
+{
+ Q_ASSERT(size > 0);
+ QRhiShaderResourceBinding b = bufferLoad(binding, stage, buf);
+ QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b);
+ d->u.sbuf.offset = offset;
+ d->u.sbuf.maybeSize = size;
+ return b;
+}
+
+/*!
+ \return a shader resource binding for a write-only storage buffer with the
+ given \a binding number and pipeline \a stage.
+
+ \note \a buf must have been created with QRhiBuffer::StorageBuffer.
+ */
+QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferStore(
+ int binding, StageFlags stage, QRhiBuffer *buf)
+{
+ QRhiShaderResourceBinding b = bufferLoad(binding, stage, buf);
+ QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b);
+ d->type = BufferStore;
+ return b;
+}
+
+/*!
+ \return a shader resource binding for a write-only storage buffer with the
+ given \a binding number and pipeline \a stage. This overload binds a region
+ only, as specified by \a offset and \a size.
+
+ \note \a buf must have been created with QRhiBuffer::StorageBuffer.
+ */
+QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferStore(
+ int binding, StageFlags stage, QRhiBuffer *buf, int offset, int size)
+{
+ Q_ASSERT(size > 0);
+ QRhiShaderResourceBinding b = bufferStore(binding, stage, buf);
+ QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b);
+ d->u.sbuf.offset = offset;
+ d->u.sbuf.maybeSize = size;
+ return b;
+}
+
+/*!
+ \return a shader resource binding for a read-write storage buffer with the
+ given \a binding number and pipeline \a stage.
+
+ \note \a buf must have been created with QRhiBuffer::StorageBuffer.
+ */
+QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferLoadStore(
+ int binding, StageFlags stage, QRhiBuffer *buf)
+{
+ QRhiShaderResourceBinding b = bufferLoad(binding, stage, buf);
+ QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b);
+ d->type = BufferLoadStore;
+ return b;
+}
+
+/*!
+ \return a shader resource binding for a read-write storage buffer with the
+ given \a binding number and pipeline \a stage. This overload binds a region
+ only, as specified by \a offset and \a size.
+
+ \note \a buf must have been created with QRhiBuffer::StorageBuffer.
+ */
+QRhiShaderResourceBinding QRhiShaderResourceBinding::bufferLoadStore(
+ int binding, StageFlags stage, QRhiBuffer *buf, int offset, int size)
+{
+ Q_ASSERT(size > 0);
+ QRhiShaderResourceBinding b = bufferLoadStore(binding, stage, buf);
+ QRhiShaderResourceBindingPrivate *d = QRhiShaderResourceBindingPrivate::get(&b);
+ d->u.sbuf.offset = offset;
+ d->u.sbuf.maybeSize = size;
+ return b;
+}
+
+/*!
+ \return \c true if the contents of the two QRhiShaderResourceBinding
+ objects \a a and \a b are equal. This includes the resources (buffer,
+ texture) and related parameters (offset, size) as well. To only compare
+ layouts (binding point, pipeline stage, resource type), use
+ \l{QRhiShaderResourceBinding::isLayoutCompatible()}{isLayoutCompatible()}
+ instead.
+
+ \relates QRhiShaderResourceBinding
+ */
+bool operator==(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b) Q_DECL_NOTHROW
+{
+ if (a.d == b.d)
+ return true;
+
+ if (a.d->binding != b.d->binding
+ || a.d->stage != b.d->stage
+ || a.d->type != b.d->type)
+ {
+ return false;
+ }
+
+ switch (a.d->type) {
+ case QRhiShaderResourceBinding::UniformBuffer:
+ if (a.d->u.ubuf.buf != b.d->u.ubuf.buf
+ || a.d->u.ubuf.offset != b.d->u.ubuf.offset
+ || a.d->u.ubuf.maybeSize != b.d->u.ubuf.maybeSize)
+ {
+ return false;
+ }
+ break;
+ case QRhiShaderResourceBinding::SampledTexture:
+ if (a.d->u.stex.tex != b.d->u.stex.tex
+ || a.d->u.stex.sampler != b.d->u.stex.sampler)
+ {
+ return false;
+ }
+ break;
+ case QRhiShaderResourceBinding::ImageLoad:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::ImageStore:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::ImageLoadStore:
+ if (a.d->u.simage.tex != b.d->u.simage.tex
+ || a.d->u.simage.level != b.d->u.simage.level)
+ {
+ return false;
+ }
+ break;
+ case QRhiShaderResourceBinding::BufferLoad:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::BufferStore:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::BufferLoadStore:
+ if (a.d->u.sbuf.buf != b.d->u.sbuf.buf
+ || a.d->u.sbuf.offset != b.d->u.sbuf.offset
+ || a.d->u.sbuf.maybeSize != b.d->u.sbuf.maybeSize)
+ {
+ return false;
+ }
+ break;
+ default:
+ Q_UNREACHABLE();
+ return false;
+ }
+
+ return true;
+}
+
+/*!
+ \return \c false if all the bindings in the two QRhiShaderResourceBinding
+ objects \a a and \a b are equal; otherwise returns \c true.
+
+ \relates QRhiShaderResourceBinding
+ */
+bool operator!=(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b) Q_DECL_NOTHROW
+{
+ return !(a == b);
+}
+
+/*!
+ \return the hash value for \a b, using \a seed to seed the calculation.
+
+ \relates QRhiShaderResourceBinding
+ */
+uint qHash(const QRhiShaderResourceBinding &b, uint seed) Q_DECL_NOTHROW
+{
+ const char *u = reinterpret_cast<const char *>(&b.d->u);
+ return seed + b.d->binding + 10 * b.d->stage + 100 * b.d->type
+ + qHash(QByteArray::fromRawData(u, sizeof(b.d->u)), seed);
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QRhiShaderResourceBinding &b)
+{
+ const QRhiShaderResourceBindingPrivate *d = b.d;
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "QRhiShaderResourceBinding("
+ << "binding=" << d->binding
+ << " stage=" << d->stage
+ << " type=" << d->type;
+ switch (d->type) {
+ case QRhiShaderResourceBinding::UniformBuffer:
+ dbg.nospace() << " UniformBuffer("
+ << "buffer=" << d->u.ubuf.buf
+ << " offset=" << d->u.ubuf.offset
+ << " maybeSize=" << d->u.ubuf.maybeSize
+ << ')';
+ break;
+ case QRhiShaderResourceBinding::SampledTexture:
+ dbg.nospace() << " SampledTexture("
+ << "texture=" << d->u.stex.tex
+ << " sampler=" << d->u.stex.sampler
+ << ')';
+ break;
+ case QRhiShaderResourceBinding::ImageLoad:
+ dbg.nospace() << " ImageLoad("
+ << "texture=" << d->u.simage.tex
+ << " level=" << d->u.simage.level
+ << ')';
+ break;
+ case QRhiShaderResourceBinding::ImageStore:
+ dbg.nospace() << " ImageStore("
+ << "texture=" << d->u.simage.tex
+ << " level=" << d->u.simage.level
+ << ')';
+ break;
+ case QRhiShaderResourceBinding::ImageLoadStore:
+ dbg.nospace() << " ImageLoadStore("
+ << "texture=" << d->u.simage.tex
+ << " level=" << d->u.simage.level
+ << ')';
+ break;
+ case QRhiShaderResourceBinding::BufferLoad:
+ dbg.nospace() << " BufferLoad("
+ << "buffer=" << d->u.sbuf.buf
+ << " offset=" << d->u.sbuf.offset
+ << " maybeSize=" << d->u.sbuf.maybeSize
+ << ')';
+ break;
+ case QRhiShaderResourceBinding::BufferStore:
+ dbg.nospace() << " BufferStore("
+ << "buffer=" << d->u.sbuf.buf
+ << " offset=" << d->u.sbuf.offset
+ << " maybeSize=" << d->u.sbuf.maybeSize
+ << ')';
+ break;
+ case QRhiShaderResourceBinding::BufferLoadStore:
+ dbg.nospace() << " BufferLoadStore("
+ << "buffer=" << d->u.sbuf.buf
+ << " offset=" << d->u.sbuf.offset
+ << " maybeSize=" << d->u.sbuf.maybeSize
+ << ')';
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ dbg.nospace() << ')';
+ return dbg;
+}
+#endif
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QRhiShaderResourceBindings &srb)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "QRhiShaderResourceBindings("
+ << srb.m_bindings
+ << ')';
+ return dbg;
+}
+#endif
+
+/*!
+ \class QRhiGraphicsPipeline
+ \inmodule QtRhi
+ \brief Graphics pipeline state resource.
+
+ \note Setting the shader resource bindings is mandatory. The referenced
+ QRhiShaderResourceBindings must already be built by the time build() is
+ called.
+
+ \note Setting the render pass descriptor is mandatory. To obtain a
+ QRhiRenderPassDescriptor that can be passed to setRenderPassDescriptor(),
+ use either QRhiTextureRenderTarget::newCompatibleRenderPassDescriptor() or
+ QRhiSwapChain::newCompatibleRenderPassDescriptor().
+
+ \note Setting the vertex input layout is mandatory.
+
+ \note Setting the shader stages is mandatory.
+
+ \note sampleCount() defaults to 1 and must match the sample count of the
+ render target's color and depth stencil attachments.
+
+ \note The depth test, depth write, and stencil test are disabled by
+ default.
+
+ \note stencilReadMask() and stencilWriteMask() apply to both faces. They
+ both default to 0xFF.
+ */
+
+/*!
+ \fn void QRhiGraphicsPipeline::setTargetBlends(const QVector<TargetBlend> &blends)
+
+ Sets the blend specification for color attachments. Each element in \a
+ blends corresponds to a color attachment of the render target.
+
+ By default no blends are set, which is a shortcut to disabling blending and
+ enabling color write for all four channels.
+ */
+
+/*!
+ \enum QRhiGraphicsPipeline::Flag
+
+ Flag values for describing the dynamic state of the pipeline. The viewport is always dynamic.
+
+ \value UsesBlendConstants Indicates that a blend color constant will be set
+ via QRhiCommandBuffer::setBlendConstants()
+
+ \value UsesStencilRef Indicates that a stencil reference value will be set
+ via QRhiCommandBuffer::setStencilRef()
+
+ \value UsesScissor Indicates that a scissor rectangle will be set via
+ QRhiCommandBuffer::setScissor()
+ */
+
+/*!
+ \enum QRhiGraphicsPipeline::Topology
+ Specifies the primitive topology
+
+ \value Triangles (default)
+ \value TriangleStrip
+ \value Lines
+ \value LineStrip
+ \value Points
+ */
+
+/*!
+ \enum QRhiGraphicsPipeline::CullMode
+ Specifies the culling mode
+
+ \value None No culling (default)
+ \value Front Cull front faces
+ \value Back Cull back faces
+ */
+
+/*!
+ \enum QRhiGraphicsPipeline::FrontFace
+ Specifies the front face winding order
+
+ \value CCW Counter clockwise (default)
+ \value CW Clockwise
+ */
+
+/*!
+ \enum QRhiGraphicsPipeline::ColorMaskComponent
+ Flag values for specifying the color write mask
+
+ \value R
+ \value G
+ \value B
+ \value A
+ */
+
+/*!
+ \enum QRhiGraphicsPipeline::BlendFactor
+ Specifies the blend factor
+
+ \value Zero
+ \value One
+ \value SrcColor
+ \value OneMinusSrcColor
+ \value DstColor
+ \value OneMinusDstColor
+ \value SrcAlpha
+ \value OneMinusSrcAlpha
+ \value DstAlpha
+ \value OneMinusDstAlpha
+ \value ConstantColor
+ \value OneMinusConstantColor
+ \value ConstantAlpha
+ \value OneMinusConstantAlpha
+ \value SrcAlphaSaturate
+ \value Src1Color
+ \value OneMinusSrc1Color
+ \value Src1Alpha
+ \value OneMinusSrc1Alpha
+ */
+
+/*!
+ \enum QRhiGraphicsPipeline::BlendOp
+ Specifies the blend operation
+
+ \value Add
+ \value Subtract
+ \value ReverseSubtract
+ \value Min
+ \value Max
+ */
+
+/*!
+ \enum QRhiGraphicsPipeline::CompareOp
+ Specifies the depth or stencil comparison function
+
+ \value Never
+ \value Less (default for depth)
+ \value Equal
+ \value LessOrEqual
+ \value Greater
+ \value NotEqual
+ \value GreaterOrEqual
+ \value Always (default for stencil)
+ */
+
+/*!
+ \enum QRhiGraphicsPipeline::StencilOp
+ Specifies the stencil operation
+
+ \value StencilZero
+ \value Keep (default)
+ \value Replace
+ \value IncrementAndClamp
+ \value DecrementAndClamp
+ \value Invert
+ \value IncrementAndWrap
+ \value DecrementAndWrap
+ */
+
+/*!
+ \class QRhiGraphicsPipeline::TargetBlend
+ \inmodule QtRhi
+ \brief Describes the blend state for one color attachment.
+
+ Defaults to color write enabled, blending disabled. The blend values are
+ set up for pre-multiplied alpha (One, OneMinusSrcAlpha, One,
+ OneMinusSrcAlpha) by default.
+ */
+
+/*!
+ \class QRhiGraphicsPipeline::StencilOpState
+ \inmodule QtRhi
+ \brief Describes the stencil operation state.
+ */
+
+/*!
+ \internal
+ */
+QRhiGraphicsPipeline::QRhiGraphicsPipeline(QRhiImplementation *rhi)
+ : QRhiResource(rhi)
+{
+}
+
+/*!
+ \return the resource type.
+ */
+QRhiResource::Type QRhiGraphicsPipeline::resourceType() const
+{
+ return GraphicsPipeline;
+}
+
+/*!
+ \fn bool QRhiGraphicsPipeline::build()
+
+ Creates the corresponding native graphics resources. If there are already
+ resources present due to an earlier build() with no corresponding
+ release(), then release() is called implicitly first.
+
+ \return \c true when successful, \c false when a graphics operation failed.
+ Regardless of the return value, calling release() is always safe.
+ */
+
+/*!
+ \fn void QRhiGraphicsPipeline::setDepthTest(bool enable)
+
+ Enables or disables depth testing. Both depth test and the writing out of
+ depth data are disabled by default.
+
+ \sa setDepthWrite()
+ */
+
+/*!
+ \fn void QRhiGraphicsPipeline::setDepthWrite(bool enable)
+
+ Controls the writing out of depth data into the depth buffer. By default
+ this is disabled. Depth write is typically enabled together with the depth
+ test.
+
+ \note Enabling depth write without having depth testing enabled may not
+ lead to the desired result, and should be avoided.
+
+ \sa setDepthTest()
+ */
+
+/*!
+ \class QRhiSwapChain
+ \inmodule QtRhi
+ \brief Swapchain resource.
+
+ A swapchain enables presenting rendering results to a surface. A swapchain
+ is typically backed by a set of color buffers. Of these, one is displayed
+ at a time.
+
+ Below is a typical pattern for creating and managing a swapchain and some
+ associated resources in order to render onto a QWindow:
+
+ \badcode
+ void init()
+ {
+ sc = rhi->newSwapChain();
+ ds = rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil,
+ QSize(), // no need to set the size yet
+ 1,
+ QRhiRenderBuffer::UsedWithSwapChainOnly);
+ sc->setWindow(window);
+ sc->setDepthStencil(ds);
+ rp = sc->newCompatibleRenderPassDescriptor();
+ sc->setRenderPassDescriptor(rp);
+ resizeSwapChain();
+ }
+
+ void resizeSwapChain()
+ {
+ const QSize outputSize = sc->surfacePixelSize();
+ ds->setPixelSize(outputSize);
+ ds->build();
+ hasSwapChain = sc->buildOrResize();
+ }
+
+ void render()
+ {
+ if (!hasSwapChain || notExposed)
+ return;
+
+ if (sc->currentPixelSize() != sc->surfacePixelSize() || newlyExposed) {
+ resizeSwapChain();
+ if (!hasSwapChain)
+ return;
+ newlyExposed = false;
+ }
+
+ rhi->beginFrame(sc);
+ // ...
+ rhi->endFrame(sc);
+ }
+ \endcode
+
+ Avoid relying on QWindow resize events to resize swapchains, especially
+ considering that surface sizes may not always fully match the QWindow
+ reported dimensions. The safe, cross-platform approach is to do the check
+ via surfacePixelSize() whenever starting a new frame.
+
+ Releasing the swapchain must happen while the QWindow and the underlying
+ native window is fully up and running. Building on the previous example:
+
+ \badcode
+ void releaseSwapChain()
+ {
+ if (hasSwapChain) {
+ sc->release();
+ hasSwapChain = false;
+ }
+ }
+
+ // assuming Window is our QWindow subclass
+ bool Window::event(QEvent *e)
+ {
+ switch (e->type()) {
+ case QEvent::UpdateRequest: // for QWindow::requestUpdate()
+ render();
+ break;
+ case QEvent::PlatformSurface:
+ if (static_cast<QPlatformSurfaceEvent *>(e)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed)
+ releaseSwapChain();
+ break;
+ default:
+ break;
+ }
+ return QWindow::event(e);
+ }
+ \endcode
+
+ Initializing the swapchain and starting to render the first frame cannot
+ start at any time. The safe, cross-platform approach is to rely on expose
+ events. QExposeEvent is a loosely specified event that is sent whenever a
+ window gets mapped, obscured, and resized, depending on the platform.
+
+ \badcode
+ void Window::exposeEvent(QExposeEvent *)
+ {
+ // initialize and start rendering when the window becomes usable for graphics purposes
+ if (isExposed() && !running) {
+ running = true;
+ init();
+ }
+
+ // stop pushing frames when not exposed or size becomes 0
+ if ((!isExposed() || (hasSwapChain && sc->surfacePixelSize().isEmpty())) && running)
+ notExposed = true;
+
+ // continue when exposed again and the surface has a valid size
+ if (isExposed() && running && notExposed && !sc->surfacePixelSize().isEmpty()) {
+ notExposed = false;
+ newlyExposed = true;
+ }
+
+ if (isExposed() && !sc->surfacePixelSize().isEmpty())
+ render();
+ }
+ \endcode
+
+ Once the rendering has started, a simple way to request a new frame is
+ QWindow::requestUpdate(). While on some platforms this is merely a small
+ timer, on others it has a specific implementation: for instance on macOS or
+ iOS it may be backed by
+ \l{https://developer.apple.com/documentation/corevideo/cvdisplaylink?language=objc}{CVDisplayLink}.
+ The example above is already prepared for update requests by handling
+ QEvent::UpdateRequest.
+
+ While acting as a QRhiRenderTarget, QRhiSwapChain also manages a
+ QRhiCommandBuffer. Calling QRhi::endFrame() submits the recorded commands
+ and also enqueues a \c present request. The default behavior is to do this
+ with a swap interval of 1, meaning synchronizing to the display's vertical
+ refresh is enabled. Thus the rendering thread calling beginFrame() and
+ endFrame() will get throttled to vsync. On some backends this can be
+ disabled by passing QRhiSwapChain:NoVSync in flags().
+
+ Multisampling (MSAA) is handled transparently to the applications when
+ requested via setSampleCount(). Where applicable, QRhiSwapChain will take
+ care of creating additional color buffers and issuing a multisample resolve
+ command at the end of a frame. For OpenGL, it is necessary to request the
+ appropriate sample count also via QSurfaceFormat, by calling
+ QSurfaceFormat::setDefaultFormat() before initializing the QRhi.
+ */
+
+/*!
+ \enum QRhiSwapChain::Flag
+ Flag values to describe swapchain properties
+
+ \value SurfaceHasPreMulAlpha Indicates that the target surface has
+ transparency with premultiplied alpha.
+
+ \value SurfaceHasNonPreMulAlpha Indicates the target surface has
+ transparencyt with non-premultiplied alpha.
+
+ \value sRGB Requests to pick an sRGB format for the swapchain and/or its
+ render target views, where applicable. Note that this implies that sRGB
+ framebuffer update and blending will get enabled for all content targeting
+ this swapchain, and opting out is not possible. For OpenGL, set
+ \l{QSurfaceFormat::sRGBColorSpace}{sRGBColorSpace} on the QSurfaceFormat of
+ the QWindow in addition.
+
+ \value UsedAsTransferSource Indicates the the swapchain will be used as the
+ source of a readback in QRhiResourceUpdateBatch::readBackTexture().
+
+ \value NoVSync Requests disabling waiting for vertical sync, also avoiding
+ throttling the rendering thread. The behavior is backend specific and
+ applicable only where it is possible to control this. Some may ignore the
+ request altogether. For OpenGL, try instead setting the swap interval to 0
+ on the QWindow via QSurfaceFormat::setSwapInterval().
+
+ \value MinimalBufferCount Requests creating the swapchain with the minimum
+ number of buffers, which is in practice 2, unless the graphics
+ implementation has a higher minimum number than that. Only applicable with
+ backends where such control is available via the graphics API, for example,
+ Vulkan. By default it is up to the backend to decide what number of buffers
+ it requests (in practice this is almost always either 2 or 3), and it is
+ not the applications' concern. However, on Vulkan for instance the backend
+ will likely prefer the higher number (3), for example to avoid odd
+ performance issues with some Vulkan implementations on mobile devices. It
+ could be that on some platforms it can prove to be beneficial to force the
+ lower buffer count (2), so this flag allows forcing that. Note that all
+ this has no effect on the number of frames kept in flight, so the CPU
+ (QRhi) will still prepare frames at most \c{N - 1} frames ahead of the GPU,
+ even when the swapchain image buffer count larger than \c N. (\c{N} =
+ QRhi::FramesInFlight and typically 2).
+ */
+
+/*!
+ \internal
+ */
+QRhiSwapChain::QRhiSwapChain(QRhiImplementation *rhi)
+ : QRhiResource(rhi)
+{
+}
+
+/*!
+ \return the resource type.
+ */
+QRhiResource::Type QRhiSwapChain::resourceType() const
+{
+ return SwapChain;
+}
+
+/*!
+ \fn QSize QRhiSwapChain::currentPixelSize() const
+
+ \return the size with which the swapchain was last successfully built. Use
+ this to decide if buildOrResize() needs to be called again: if
+ \c{currentPixelSize() != surfacePixelSize()} then the swapchain needs to be
+ resized.
+
+ \sa surfacePixelSize()
+ */
+
+/*!
+ \fn QSize QRhiSwapChain::surfacePixelSize()
+
+ \return The size of the window's associated surface or layer. Do not assume
+ this is the same as QWindow::size() * QWindow::devicePixelRatio().
+
+ Can be called before buildOrResize() (but with window() already set), which
+ allows setting the correct size for the depth-stencil buffer that is then
+ used together with the swapchain's color buffers. Also used in combination
+ with currentPixelSize() to detect size changes.
+
+ \sa currentPixelSize()
+ */
+
+/*!
+ \fn QRhiCommandBuffer *QRhiSwapChain::currentFrameCommandBuffer()
+
+ \return a command buffer on which rendering commands can be recorded. Only
+ valid within a QRhi::beginFrame() - QRhi::endFrame() block where
+ beginFrame() was called with this swapchain.
+
+ \note the value must not be cached and reused between frames
+*/
+
+/*!
+ \fn QRhiRenderTarget *QRhiSwapChain::currentFrameRenderTarget()
+
+ \return a render target that can used with beginPass() in order to render
+ the the swapchain's current backbuffer. Only valid within a
+ QRhi::beginFrame() - QRhi::endFrame() block where beginFrame() was called
+ with this swapchain.
+
+ \note the value must not be cached and reused between frames
+ */
+
+/*!
+ \fn bool QRhiSwapChain::buildOrResize()
+
+ Creates the swapchain if not already done and resizes the swapchain buffers
+ to match the current size of the targeted surface. Call this whenever the
+ size of the target surface is different than before.
+
+ \note call release() only when the swapchain needs to be released
+ completely, typically upon
+ QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed. To perform resizing, just
+ call buildOrResize().
+
+ \return \c true when successful, \c false when a graphics operation failed.
+ Regardless of the return value, calling release() is always safe.
+ */
+
+/*!
+ \class QRhiComputePipeline
+ \inmodule QtRhi
+ \brief Compute pipeline state resource.
+
+ \note Setting the shader resource bindings is mandatory. The referenced
+ QRhiShaderResourceBindings must already be built by the time build() is
+ called.
+
+ \note Setting the shader is mandatory.
+ */
+
+/*!
+ \return the resource type.
+ */
+QRhiResource::Type QRhiComputePipeline::resourceType() const
+{
+ return ComputePipeline;
+}
+
+/*!
+ \internal
+ */
+QRhiComputePipeline::QRhiComputePipeline(QRhiImplementation *rhi)
+ : QRhiResource(rhi)
+{
+}
+
+/*!
+ \class QRhiCommandBuffer
+ \inmodule QtRhi
+ \brief Command buffer resource.
+
+ Not creatable by applications at the moment. The only ways to obtain a
+ valid QRhiCommandBuffer are to get it from the targeted swapchain via
+ QRhiSwapChain::currentFrameCommandBuffer(), or, in case of rendering
+ completely offscreen, initializing one via QRhi::beginOffscreenFrame().
+ */
+
+/*!
+ \enum QRhiCommandBuffer::IndexFormat
+ Specifies the index data type
+
+ \value IndexUInt16 Unsigned 16-bit (quint16)
+ \value IndexUInt32 Unsigned 32-bit (quint32)
+ */
+
+/*!
+ \typedef QRhiCommandBuffer::DynamicOffset
+
+ Synonym for QPair<int, quint32>. The first entry is the binding, the second
+ is the offset in the buffer.
+*/
+
+/*!
+ \typedef QRhiCommandBuffer::VertexInput
+
+ Synonym for QPair<QRhiBuffer *, quint32>. The second entry is an offset in
+ the buffer specified by the first.
+*/
+
+/*!
+ \internal
+ */
+QRhiCommandBuffer::QRhiCommandBuffer(QRhiImplementation *rhi)
+ : QRhiResource(rhi)
+{
+}
+
+/*!
+ \return the resource type.
+ */
+QRhiResource::Type QRhiCommandBuffer::resourceType() const
+{
+ return CommandBuffer;
+}
+
+QRhiImplementation::~QRhiImplementation()
+{
+ qDeleteAll(resUpdPool);
+
+ // Be nice and show something about leaked stuff. Though we may not get
+ // this far with some backends where the allocator or the api may check
+ // and freak out for unfreed graphics objects in the derived dtor already.
+#ifndef QT_NO_DEBUG
+ if (!resources.isEmpty()) {
+ qWarning("QRhi %p going down with %d unreleased resources. This is not nice.",
+ q, resources.count());
+ for (QRhiResource *res : qAsConst(resources)) {
+ qWarning(" Resource %p (%s)", res, res->m_objectName.constData());
+ res->m_rhi = nullptr;
+ }
+ }
+#endif
+}
+
+bool QRhiImplementation::isCompressedFormat(QRhiTexture::Format format) const
+{
+ return (format >= QRhiTexture::BC1 && format <= QRhiTexture::BC7)
+ || (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ETC2_RGBA8)
+ || (format >= QRhiTexture::ASTC_4x4 && format <= QRhiTexture::ASTC_12x12);
+}
+
+void QRhiImplementation::compressedFormatInfo(QRhiTexture::Format format, const QSize &size,
+ quint32 *bpl, quint32 *byteSize,
+ QSize *blockDim) const
+{
+ int xdim = 4;
+ int ydim = 4;
+ quint32 blockSize = 0;
+
+ switch (format) {
+ case QRhiTexture::BC1:
+ blockSize = 8;
+ break;
+ case QRhiTexture::BC2:
+ blockSize = 16;
+ break;
+ case QRhiTexture::BC3:
+ blockSize = 16;
+ break;
+ case QRhiTexture::BC4:
+ blockSize = 8;
+ break;
+ case QRhiTexture::BC5:
+ blockSize = 16;
+ break;
+ case QRhiTexture::BC6H:
+ blockSize = 16;
+ break;
+ case QRhiTexture::BC7:
+ blockSize = 16;
+ break;
+
+ case QRhiTexture::ETC2_RGB8:
+ blockSize = 8;
+ break;
+ case QRhiTexture::ETC2_RGB8A1:
+ blockSize = 8;
+ break;
+ case QRhiTexture::ETC2_RGBA8:
+ blockSize = 16;
+ break;
+
+ case QRhiTexture::ASTC_4x4:
+ blockSize = 16;
+ break;
+ case QRhiTexture::ASTC_5x4:
+ blockSize = 16;
+ xdim = 5;
+ break;
+ case QRhiTexture::ASTC_5x5:
+ blockSize = 16;
+ xdim = ydim = 5;
+ break;
+ case QRhiTexture::ASTC_6x5:
+ blockSize = 16;
+ xdim = 6;
+ ydim = 5;
+ break;
+ case QRhiTexture::ASTC_6x6:
+ blockSize = 16;
+ xdim = ydim = 6;
+ break;
+ case QRhiTexture::ASTC_8x5:
+ blockSize = 16;
+ xdim = 8;
+ ydim = 5;
+ break;
+ case QRhiTexture::ASTC_8x6:
+ blockSize = 16;
+ xdim = 8;
+ ydim = 6;
+ break;
+ case QRhiTexture::ASTC_8x8:
+ blockSize = 16;
+ xdim = ydim = 8;
+ break;
+ case QRhiTexture::ASTC_10x5:
+ blockSize = 16;
+ xdim = 10;
+ ydim = 5;
+ break;
+ case QRhiTexture::ASTC_10x6:
+ blockSize = 16;
+ xdim = 10;
+ ydim = 6;
+ break;
+ case QRhiTexture::ASTC_10x8:
+ blockSize = 16;
+ xdim = 10;
+ ydim = 8;
+ break;
+ case QRhiTexture::ASTC_10x10:
+ blockSize = 16;
+ xdim = ydim = 10;
+ break;
+ case QRhiTexture::ASTC_12x10:
+ blockSize = 16;
+ xdim = 12;
+ ydim = 10;
+ break;
+ case QRhiTexture::ASTC_12x12:
+ blockSize = 16;
+ xdim = ydim = 12;
+ break;
+
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ const quint32 wblocks = (size.width() + xdim - 1) / xdim;
+ const quint32 hblocks = (size.height() + ydim - 1) / ydim;
+
+ if (bpl)
+ *bpl = wblocks * blockSize;
+ if (byteSize)
+ *byteSize = wblocks * hblocks * blockSize;
+ if (blockDim)
+ *blockDim = QSize(xdim, ydim);
+}
+
+void QRhiImplementation::textureFormatInfo(QRhiTexture::Format format, const QSize &size,
+ quint32 *bpl, quint32 *byteSize) const
+{
+ if (isCompressedFormat(format)) {
+ compressedFormatInfo(format, size, bpl, byteSize, nullptr);
+ return;
+ }
+
+ quint32 bpc = 0;
+ switch (format) {
+ case QRhiTexture::RGBA8:
+ bpc = 4;
+ break;
+ case QRhiTexture::BGRA8:
+ bpc = 4;
+ break;
+ case QRhiTexture::R8:
+ bpc = 1;
+ break;
+ case QRhiTexture::R16:
+ bpc = 2;
+ break;
+ case QRhiTexture::RED_OR_ALPHA8:
+ bpc = 1;
+ break;
+
+ case QRhiTexture::RGBA16F:
+ bpc = 8;
+ break;
+ case QRhiTexture::RGBA32F:
+ bpc = 16;
+ break;
+
+ case QRhiTexture::D16:
+ bpc = 2;
+ break;
+ case QRhiTexture::D32F:
+ bpc = 4;
+ break;
+
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ if (bpl)
+ *bpl = size.width() * bpc;
+ if (byteSize)
+ *byteSize = size.width() * size.height() * bpc;
+}
+
+// Approximate because it excludes subresource alignment or multisampling.
+quint32 QRhiImplementation::approxByteSizeForTexture(QRhiTexture::Format format, const QSize &baseSize,
+ int mipCount, int layerCount)
+{
+ quint32 approxSize = 0;
+ for (int level = 0; level < mipCount; ++level) {
+ quint32 byteSize = 0;
+ const QSize size(qFloor(float(qMax(1, baseSize.width() >> level))),
+ qFloor(float(qMax(1, baseSize.height() >> level))));
+ textureFormatInfo(format, size, nullptr, &byteSize);
+ approxSize += byteSize;
+ }
+ approxSize *= layerCount;
+ return approxSize;
+}
+
+/*!
+ \internal
+ */
+QRhi::QRhi()
+{
+}
+
+/*!
+ Destructor. Destroys the backend and releases resources.
+ */
+QRhi::~QRhi()
+{
+ if (!d)
+ return;
+
+ qDeleteAll(d->pendingReleaseAndDestroyResources);
+ d->pendingReleaseAndDestroyResources.clear();
+
+ runCleanup();
+
+ d->destroy();
+ delete d;
+}
+
+/*!
+ \return a new QRhi instance with a backend for the graphics API specified by \a impl.
+
+ \a params must point to an instance of one of the backend-specific
+ subclasses of QRhiInitParams, such as, QRhiVulkanInitParams,
+ QRhiMetalInitParams, QRhiD3D11InitParams, QRhiGles2InitParams. See these
+ classes for examples on creating a QRhi.
+
+ \a flags is optional. It is used to enable profile and debug related
+ features that are potentially expensive and should only be used during
+ development.
+ */
+QRhi *QRhi::create(Implementation impl, QRhiInitParams *params, Flags flags, QRhiNativeHandles *importDevice)
+{
+ QScopedPointer<QRhi> r(new QRhi);
+
+ switch (impl) {
+ case Null:
+ r->d = new QRhiNull(static_cast<QRhiNullInitParams *>(params));
+ break;
+ case Vulkan:
+#if QT_CONFIG(vulkan)
+ r->d = new QRhiVulkan(static_cast<QRhiVulkanInitParams *>(params),
+ static_cast<QRhiVulkanNativeHandles *>(importDevice));
+ break;
+#else
+ qWarning("This build of Qt has no Vulkan support");
+ break;
+#endif
+ case OpenGLES2:
+#ifndef QT_NO_OPENGL
+ r->d = new QRhiGles2(static_cast<QRhiGles2InitParams *>(params),
+ static_cast<QRhiGles2NativeHandles *>(importDevice));
+ break;
+#else
+ qWarning("This build of Qt has no OpenGL support");
+ break;
+#endif
+ case D3D11:
+#ifdef Q_OS_WIN
+ r->d = new QRhiD3D11(static_cast<QRhiD3D11InitParams *>(params),
+ static_cast<QRhiD3D11NativeHandles *>(importDevice));
+ break;
+#else
+ qWarning("This platform has no Direct3D 11 support");
+ break;
+#endif
+ case Metal:
+//#ifdef Q_OS_DARWIN
+#ifdef Q_OS_MACOS
+ r->d = new QRhiMetal(static_cast<QRhiMetalInitParams *>(params),
+ static_cast<QRhiMetalNativeHandles *>(importDevice));
+ break;
+#else
+ qWarning("This platform has no Metal support");
+ break;
+#endif
+ default:
+ break;
+ }
+
+ if (r->d) {
+ r->d->q = r.data();
+ if (flags.testFlag(EnableProfiling)) {
+ QRhiProfilerPrivate *profD = QRhiProfilerPrivate::get(&r->d->profiler);
+ profD->rhiDWhenEnabled = r->d;
+ }
+ r->d->debugMarkers = flags.testFlag(EnableDebugMarkers);
+ if (r->d->create(flags)) {
+ r->d->implType = impl;
+ r->d->implThread = QThread::currentThread();
+ return r.take();
+ }
+ }
+
+ return nullptr;
+}
+
+/*!
+ \return the backend type for this QRhi.
+ */
+QRhi::Implementation QRhi::backend() const
+{
+ return d->implType;
+}
+
+/*!
+ \return the thread on which the QRhi was \l{QRhi::create()}{initialized}.
+ */
+QThread *QRhi::thread() const
+{
+ return d->implThread;
+}
+
+/*!
+ Registers a \a callback that is invoked either when the QRhi is destroyed,
+ or when runCleanup() is called.
+
+ The callback will run with the graphics resource still available, so this
+ provides an opportunity for the application to cleanly release QRhiResource
+ instances belonging to the QRhi. This is particularly useful for managing
+ the lifetime of resources stored in \c cache type of objects, where the
+ cache holds QRhiResources or objects containing QRhiResources.
+
+ \sa runCleanup(), ~QRhi()
+ */
+void QRhi::addCleanupCallback(const CleanupCallback &callback)
+{
+ d->addCleanupCallback(callback);
+}
+
+/*!
+ Invokes all registered cleanup functions. The list of cleanup callbacks it
+ then cleared. Normally destroying the QRhi does this automatically, but
+ sometimes it can be useful to trigger cleanup in order to release all
+ cached, non-essential resources.
+
+ \sa addCleanupCallback()
+ */
+void QRhi::runCleanup()
+{
+ for (const CleanupCallback &f : qAsConst(d->cleanupCallbacks))
+ f(this);
+
+ d->cleanupCallbacks.clear();
+}
+
+/*!
+ \class QRhiResourceUpdateBatch
+ \inmodule QtRhi
+ \brief Records upload and copy type of operations.
+
+ With QRhi it is no longer possible to perform copy type of operations at
+ arbitrary times. Instead, all such operations are recorded into batches
+ that are then passed, most commonly, to QRhiCommandBuffer::beginPass().
+ What then happens under the hood is hidden from the application: the
+ underlying implementations can defer and implement these operations in
+ various different ways.
+
+ A resource update batch owns no graphics resources and does not perform any
+ actual operations on its own. It should rather be viewed as a command
+ buffer for update, upload, and copy type of commands.
+
+ To get an available, empty batch from the pool, call
+ QRhi::nextResourceUpdateBatch().
+ */
+
+/*!
+ \internal
+ */
+QRhiResourceUpdateBatch::QRhiResourceUpdateBatch(QRhiImplementation *rhi)
+ : d(new QRhiResourceUpdateBatchPrivate)
+{
+ d->q = this;
+ d->rhi = rhi;
+}
+
+QRhiResourceUpdateBatch::~QRhiResourceUpdateBatch()
+{
+ delete d;
+}
+
+/*!
+ \return the batch to the pool. This should only be used when the batch is
+ not passed to one of QRhiCommandBuffer::beginPass(),
+ QRhiCommandBuffer::endPass(), or QRhiCommandBuffer::resourceUpdate()
+ because these implicitly call release().
+
+ \note QRhiResourceUpdateBatch instances must never by \c deleted by
+ applications.
+ */
+void QRhiResourceUpdateBatch::release()
+{
+ d->free();
+}
+
+/*!
+ Copies all queued operations from the \a other batch into this one.
+
+ \note \a other is not changed in any way, typically it will still need a
+ release()
+
+ This allows for a convenient pattern where resource updates that are
+ already known during the initialization step are collected into a batch
+ that is then merged into another when starting to first render pass later
+ on:
+
+ \badcode
+ void init()
+ {
+ ...
+ initialUpdates = rhi->nextResourceUpdateBatch();
+ initialUpdates->uploadStaticBuffer(vbuf, vertexData);
+ initialUpdates->uploadStaticBuffer(ibuf, indexData);
+ ...
+ }
+
+ void render()
+ {
+ ...
+ QRhiResourceUpdateBatch *resUpdates = rhi->nextResourceUpdateBatch();
+ if (initialUpdates) {
+ resUpdates->merge(initialUpdates);
+ initialUpdates->release();
+ initialUpdates = nullptr;
+ }
+ resUpdates->updateDynamicBuffer(...);
+ ...
+ cb->beginPass(rt, clearCol, clearDs, resUpdates);
+ }
+ \endcode
+ */
+void QRhiResourceUpdateBatch::merge(QRhiResourceUpdateBatch *other)
+{
+ d->merge(other->d);
+}
+
+/*!
+ Enqueues updating a region of a QRhiBuffer \a buf created with the type
+ QRhiBuffer::Dynamic.
+
+ The region is specified \a offset and \a size. The actual bytes to write
+ are specified by \a data which must have at least \a size bytes available.
+ \a data can safely be destroyed or changed once this function returns.
+
+ \note If host writes are involved, which is the case with
+ updateDynamicBuffer() typically as such buffers are backed by host visible
+ memory with most backends, they may accumulate within a frame. Thus pass 1
+ reading a region changed by a batch passed to pass 2 may see the changes
+ specified in pass 2's update batch.
+
+ \note QRhi transparently manages double buffering in order to prevent
+ stalling the graphics pipeline. The fact that a QRhiBuffer may have
+ multiple native underneath can be safely ignored when using the QRhi and
+ QRhiResourceUpdateBatch.
+ */
+void QRhiResourceUpdateBatch::updateDynamicBuffer(QRhiBuffer *buf, int offset, int size, const void *data)
+{
+ if (size > 0)
+ d->dynamicBufferUpdates.append({ buf, offset, size, data });
+}
+
+/*!
+ Enqueues updating a region of a QRhiBuffer \a buf created with the type
+ QRhiBuffer::Immutable or QRhiBuffer::Static.
+
+ The region is specified \a offset and \a size. The actual bytes to write
+ are specified by \a data which must have at least \a size bytes available.
+ \a data can safely be destroyed or changed once this function returns.
+ */
+void QRhiResourceUpdateBatch::uploadStaticBuffer(QRhiBuffer *buf, int offset, int size, const void *data)
+{
+ if (size > 0)
+ d->staticBufferUploads.append({ buf, offset, size, data });
+}
+
+/*!
+ Enqueues updating the entire QRhiBuffer \a buf created with the type
+ QRhiBuffer::Immutable or QRhiBuffer::Static.
+ */
+void QRhiResourceUpdateBatch::uploadStaticBuffer(QRhiBuffer *buf, const void *data)
+{
+ if (buf->size() > 0)
+ d->staticBufferUploads.append({ buf, 0, 0, data });
+}
+
+/*!
+ Enqueues uploading the image data for one or more mip levels in one or more
+ layers of the texture \a tex.
+
+ The details of the copy (source QImage or compressed texture data, regions,
+ target layers and levels) are described in \a desc.
+ */
+void QRhiResourceUpdateBatch::uploadTexture(QRhiTexture *tex, const QRhiTextureUploadDescription &desc)
+{
+ if (!desc.entries().isEmpty())
+ d->textureOps.append(QRhiResourceUpdateBatchPrivate::TextureOp::textureUpload(tex, desc));
+}
+
+/*!
+ Enqueues uploading the image data for mip level 0 of layer 0 of the texture
+ \a tex.
+
+ \a tex must have an uncompressed format. Its format must also be compatible
+ with the QImage::format() of \a image. The source data is given in \a
+ image.
+ */
+void QRhiResourceUpdateBatch::uploadTexture(QRhiTexture *tex, const QImage &image)
+{
+ uploadTexture(tex, QRhiTextureUploadEntry(0, 0, image));
+}
+
+/*!
+ Enqueues a texture-to-texture copy operation from \a src into \a dst as
+ described by \a desc.
+
+ \note The source texture \a src must be created with
+ QRhiTexture::UsedAsTransferSource.
+ */
+void QRhiResourceUpdateBatch::copyTexture(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc)
+{
+ d->textureOps.append(QRhiResourceUpdateBatchPrivate::TextureOp::textureCopy(dst, src, desc));
+}
+
+/*!
+ Enqueues a texture-to-host copy operation as described by \a rb.
+
+ Normally \a rb will specify a QRhiTexture as the source. However, when the
+ swapchain in the current frame was created with
+ QRhiSwapChain::UsedAsTransferSource, it can also be the source of the
+ readback. For this, leave the texture set to null in \a rb.
+
+ Unlike other operations, the results here need to be processed by the
+ application. Therefore, \a result provides not just the data but also a
+ callback as operations on the batch are asynchronous by nature:
+
+ \badcode
+ beginFrame(sc);
+ beginPass
+ ...
+ QRhiReadbackResult *rbResult = new QRhiReadbackResult;
+ rbResult->completed = [rbResult] {
+ {
+ const QImage::Format fmt = QImage::Format_RGBA8888_Premultiplied; // fits QRhiTexture::RGBA8
+ const uchar *p = reinterpret_cast<const uchar *>(rbResult->data.constData());
+ QImage image(p, rbResult->pixelSize.width(), rbResult->pixelSize.height(), fmt);
+ image.save("result.png");
+ }
+ delete rbResult;
+ };
+ u = nextResourceUpdateBatch();
+ QRhiReadbackDescription rb; // no texture -> uses the current backbuffer of sc
+ u->readBackTexture(rb, rbResult);
+ endPass(u);
+ endFrame(sc);
+ \endcode
+
+ \note The texture must be created with QRhiTexture::UsedAsTransferSource.
+
+ \note Multisample textures cannot be read back.
+
+ \note The readback returns raw byte data, in order to allow the applications
+ to interpret it in any way they see fit. Be aware of the blending settings
+ of rendering code: if the blending is set up to rely on premultiplied alpha,
+ the results of the readback must also be interpreted as Premultiplied.
+
+ \note When interpreting the resulting raw data, be aware that the readback
+ happens with a byte ordered format. A \l{QRhiTexture::RGBA8}{RGBA8} texture
+ maps therefore to byte ordered QImage formats, such as,
+ QImage::Format_RGBA8888.
+ */
+void QRhiResourceUpdateBatch::readBackTexture(const QRhiReadbackDescription &rb, QRhiReadbackResult *result)
+{
+ d->textureOps.append(QRhiResourceUpdateBatchPrivate::TextureOp::textureRead(rb, result));
+}
+
+/*!
+ Enqueues a mipmap generation operation for the specified \a layer of texture
+ \a tex.
+
+ \note The texture must be created with QRhiTexture::MipMapped and
+ QRhiTexture::UsedWithGenerateMips.
+ */
+void QRhiResourceUpdateBatch::generateMips(QRhiTexture *tex, int layer)
+{
+ d->textureOps.append(QRhiResourceUpdateBatchPrivate::TextureOp::textureMipGen(tex, layer));
+}
+
+/*!
+ \return an available, empty batch to which copy type of operations can be
+ recorded.
+
+ \note the return value is not owned by the caller and must never be
+ destroyed. Instead, the batch is returned the the pool for reuse by passing
+ it to QRhiCommandBuffer::beginPass(), QRhiCommandBuffer::endPass(), or
+ QRhiCommandBuffer::resourceUpdate(), or by calling
+ QRhiResourceUpdateBatch::release() on it.
+
+ \note Can be called outside beginFrame() - endFrame() as well since a batch
+ instance just collects data on its own, it does not perform any operations.
+ */
+QRhiResourceUpdateBatch *QRhi::nextResourceUpdateBatch()
+{
+ auto nextFreeBatch = [this]() -> QRhiResourceUpdateBatch * {
+ for (int i = 0, ie = d->resUpdPoolMap.count(); i != ie; ++i) {
+ if (!d->resUpdPoolMap.testBit(i)) {
+ d->resUpdPoolMap.setBit(i);
+ QRhiResourceUpdateBatch *u = d->resUpdPool[i];
+ QRhiResourceUpdateBatchPrivate::get(u)->poolIndex = i;
+ return u;
+ }
+ }
+ return nullptr;
+ };
+
+ QRhiResourceUpdateBatch *u = nextFreeBatch();
+ if (!u) {
+ const int oldSize = d->resUpdPool.count();
+ const int newSize = oldSize + 4;
+ d->resUpdPool.resize(newSize);
+ d->resUpdPoolMap.resize(newSize);
+ for (int i = oldSize; i < newSize; ++i)
+ d->resUpdPool[i] = new QRhiResourceUpdateBatch(d);
+ u = nextFreeBatch();
+ Q_ASSERT(u);
+ }
+
+ return u;
+}
+
+void QRhiResourceUpdateBatchPrivate::free()
+{
+ Q_ASSERT(poolIndex >= 0 && rhi->resUpdPool[poolIndex] == q);
+
+ dynamicBufferUpdates.clear();
+ staticBufferUploads.clear();
+ textureOps.clear();
+
+ rhi->resUpdPoolMap.clearBit(poolIndex);
+ poolIndex = -1;
+}
+
+void QRhiResourceUpdateBatchPrivate::merge(QRhiResourceUpdateBatchPrivate *other)
+{
+ dynamicBufferUpdates += other->dynamicBufferUpdates;
+ staticBufferUploads += other->staticBufferUploads;
+ textureOps += other->textureOps;
+}
+
+/*!
+ Sometimes committing resource updates is necessary without starting a
+ render pass. Not often needed, updates should typically be passed to
+ beginPass (or endPass, in case of readbacks) instead.
+
+ \note Cannot be called inside a pass.
+ */
+void QRhiCommandBuffer::resourceUpdate(QRhiResourceUpdateBatch *resourceUpdates)
+{
+ if (resourceUpdates)
+ m_rhi->resourceUpdate(this, resourceUpdates);
+}
+
+/*!
+ Records starting a new render pass targeting the render target \a rt.
+
+ \a resourceUpdates, when not null, specifies a resource update batch that
+ is to be committed and then released.
+
+ The color and depth/stencil buffers of the render target are normally
+ cleared. The clear values are specified in \a colorClearValue and \a
+ depthStencilClearValue. The exception is when the render target was created
+ with QRhiTextureRenderTarget::PreserveColorContents and/or
+ QRhiTextureRenderTarget::PreserveDepthStencilContents. The clear values are
+ ignored then.
+
+ \note Enabling preserved color or depth contents leads to decreased
+ performance depending on the underlying hardware. Mobile GPUs with tiled
+ architecture benefit from not having to reload the previous contents into
+ the tile buffer. Similarly, a QRhiTextureRenderTarget with a QRhiTexture as
+ the depth buffer is less efficient than a QRhiRenderBuffer since using a
+ depth texture triggers requiring writing the data out to it, while with
+ renderbuffers this is not needed (as the API does not allow sampling or
+ reading from a renderbuffer).
+
+ \note Do not assume that any state or resource bindings persist between
+ passes.
+
+ \note The QRhiCommandBuffer's \c set and \c draw functions can only be
+ called inside a pass. Also, with the exception of setGraphicsPipeline(),
+ they expect to have a pipeline set already on the command buffer.
+ Unspecified issues may arise otherwise, depending on the backend.
+ */
+void QRhiCommandBuffer::beginPass(QRhiRenderTarget *rt,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ QRhiResourceUpdateBatch *resourceUpdates)
+{
+ m_rhi->beginPass(this, rt, colorClearValue, depthStencilClearValue, resourceUpdates);
+}
+
+/*!
+ Records ending the current render pass.
+
+ \a resourceUpdates, when not null, specifies a resource update batch that
+ is to be committed and then released.
+ */
+void QRhiCommandBuffer::endPass(QRhiResourceUpdateBatch *resourceUpdates)
+{
+ m_rhi->endPass(this, resourceUpdates);
+}
+
+/*!
+ Records setting a new graphics pipeline \a ps.
+
+ \note This function must be called before recording other \c set or \c draw
+ commands on the command buffer.
+
+ \note QRhi will optimize out unnecessary invocations within a pass, so
+ therefore overoptimizing to avoid calls to this function is not necessary
+ on the applications' side.
+
+ \note This function can only be called inside a render pass, meaning
+ between a beginPass() and endPass() call.
+ */
+void QRhiCommandBuffer::setGraphicsPipeline(QRhiGraphicsPipeline *ps)
+{
+ m_rhi->setGraphicsPipeline(this, ps);
+}
+
+/*!
+ Records binding a set of shader resources, such as, uniform buffers or
+ textures, that are made visible to one or more shader stages.
+
+ \a srb can be null in which case the current graphics or compute pipeline's
+ associated QRhiShaderResourceBindings is used. When \a srb is non-null, it
+ must be
+ \l{QRhiShaderResourceBindings::isLayoutCompatible()}{layout-compatible},
+ meaning the layout (number of bindings, the type and binding number of each
+ binding) must fully match the QRhiShaderResourceBindings that was
+ associated with the pipeline at the time of calling the pipeline's build().
+
+ There are cases when a seemingly unnecessary setShaderResources() call is
+ mandatory: when rebuilding a resource referenced from \a srb, for example
+ changing the size of a QRhiBuffer followed by a QRhiBuffer::build(), this
+ is the place where associated native objects (such as descriptor sets in
+ case of Vulkan) are updated to refer to the current native resources that
+ back the QRhiBuffer, QRhiTexture, QRhiSampler objects referenced from \a
+ srb. In this case setShaderResources() must be called even if \a srb is
+ the same as in the last call.
+
+ \a dynamicOffsets allows specifying buffer offsets for uniform buffers that
+ were associated with \a srb via
+ QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(). This is
+ different from providing the offset in the \a srb itself: dynamic offsets
+ do not require building a new QRhiShaderResourceBindings for every
+ different offset, can avoid writing the underlying descriptors (with
+ backends where applicable), and so they may be more efficient. Each element
+ of \a dynamicOffsets is a \c binding - \c offset pair.
+ \a dynamicOffsetCount specifies the number of elements in \a dynamicOffsets.
+
+ \note All offsets in \a dynamicOffsets must be byte aligned to the value
+ returned from QRhi::ubufAlignment().
+
+ \note QRhi will optimize out unnecessary invocations within a pass (taking
+ the conditions described above into account), so therefore overoptimizing
+ to avoid calls to this function is not necessary on the applications' side.
+
+ \note This function can only be called inside a render or compute pass,
+ meaning between a beginPass() and endPass(), or beginComputePass() and
+ endComputePass().
+ */
+void QRhiCommandBuffer::setShaderResources(QRhiShaderResourceBindings *srb,
+ int dynamicOffsetCount,
+ const DynamicOffset *dynamicOffsets)
+{
+ m_rhi->setShaderResources(this, srb, dynamicOffsetCount, dynamicOffsets);
+}
+
+/*!
+ Records vertex input bindings.
+
+ The index buffer used by subsequent drawIndexed() commands is specified by
+ \a indexBuf, \a indexOffset, and \a indexFormat. \a indexBuf can be set to
+ null when indexed drawing is not needed.
+
+ Vertex buffer bindings are batched. \a startBinding specifies the first
+ binding number. The recorded command then binds each buffer from \a
+ bindings to the binding point \c{startBinding + i} where \c i is the index
+ in \a bindings. Each element in \a bindings specifies a QRhiBuffer and an
+ offset.
+
+ Superfluous vertex input and index changes in the same pass are ignored
+ automatically with most backends and therefore applications do not need to
+ overoptimize to avoid calls to this function.
+
+ \note This function can only be called inside a render pass, meaning
+ between a beginPass() and endPass() call.
+
+ As a simple example, take a vertex shader with two inputs:
+
+ \badcode
+ layout(location = 0) in vec4 position;
+ layout(location = 1) in vec3 color;
+ \endcode
+
+ and assume we have the data available in interleaved format, using only 2
+ floats for position (so 5 floats per vertex: x, y, r, g, b). A QRhiGraphicsPipeline for
+ this shader can then be created using the input layout:
+
+ \badcode
+ QRhiVertexInputLayout inputLayout;
+ inputLayout.setBindings({
+ { 5 * sizeof(float) }
+ });
+ inputLayout.setAttributes({
+ { 0, 0, QRhiVertexInputAttribute::Float2, 0 },
+ { 0, 1, QRhiVertexInputAttribute::Float3, 2 * sizeof(float) }
+ });
+ \endcode
+
+ Here there is one buffer binding (binding number 0), with two inputs
+ referencing it. When recording the pass, once the pipeline is set, the
+ vertex bindings can be specified simply like the following (using C++11
+ initializer syntax), assuming vbuf is the QRhiBuffer with all the
+ interleaved position+color data:
+
+ \badcode
+ const QRhiCommandBuffer::VertexInput vbufBinding(vbuf, 0);
+ cb->setVertexInput(0, 1, &vbufBinding);
+ \endcode
+ */
+void QRhiCommandBuffer::setVertexInput(int startBinding, int bindingCount, const VertexInput *bindings,
+ QRhiBuffer *indexBuf, quint32 indexOffset,
+ IndexFormat indexFormat)
+{
+ m_rhi->setVertexInput(this, startBinding, bindingCount, bindings, indexBuf, indexOffset, indexFormat);
+}
+
+/*!
+ Records setting the active viewport rectangle specified in \a viewport.
+
+ With backends where the underlying graphics API has scissoring always
+ enabled, this function also sets the scissor to match the viewport whenever
+ the active QRhiGraphicsPipeline does not have
+ \l{QRhiGraphicsPipeline::UsesScissor}{UsesScissor} set.
+
+ \note QRhi assumes OpenGL-style viewport coordinates, meaning x and y are
+ bottom-left.
+
+ \note This function can only be called inside a render pass, meaning
+ between a beginPass() and endPass() call.
+ */
+void QRhiCommandBuffer::setViewport(const QRhiViewport &viewport)
+{
+ m_rhi->setViewport(this, viewport);
+}
+
+/*!
+ Records setting the active scissor rectangle specified in \a scissor.
+
+ This can only be called when the bound pipeline has
+ \l{QRhiGraphicsPipeline::UsesScissor}{UsesScissor} set. When the flag is
+ set on the active pipeline, this function must be called because scissor
+ testing will get enabled and so a scissor rectangle must be provided.
+
+ \note QRhi assumes OpenGL-style viewport coordinates, meaning x and y are
+ bottom-left.
+
+ \note This function can only be called inside a render pass, meaning
+ between a beginPass() and endPass() call.
+ */
+void QRhiCommandBuffer::setScissor(const QRhiScissor &scissor)
+{
+ m_rhi->setScissor(this, scissor);
+}
+
+/*!
+ Records setting the active blend constants to \a c.
+
+ This can only be called when the bound pipeline has
+ QRhiGraphicsPipeline::UsesBlendConstants set.
+
+ \note This function can only be called inside a render pass, meaning
+ between a beginPass() and endPass() call.
+ */
+void QRhiCommandBuffer::setBlendConstants(const QColor &c)
+{
+ m_rhi->setBlendConstants(this, c);
+}
+
+/*!
+ Records setting the active stencil reference value to \a refValue.
+
+ This can only be called when the bound pipeline has
+ QRhiGraphicsPipeline::UsesStencilRef set.
+
+ \note This function can only be called inside a render pass, meaning between
+ a beginPass() and endPass() call.
+ */
+void QRhiCommandBuffer::setStencilRef(quint32 refValue)
+{
+ m_rhi->setStencilRef(this, refValue);
+}
+
+/*!
+ Records a non-indexed draw.
+
+ The number of vertices is specified in \a vertexCount. For instanced
+ drawing set \a instanceCount to a value other than 1. \a firstVertex is
+ the index of the first vertex to draw. \a firstInstance is the instance ID
+ of the first instance to draw.
+
+ \note This function can only be called inside a render pass, meaning
+ between a beginPass() and endPass() call.
+ */
+void QRhiCommandBuffer::draw(quint32 vertexCount,
+ quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
+{
+ m_rhi->draw(this, vertexCount, instanceCount, firstVertex, firstInstance);
+}
+
+/*!
+ Records an indexed draw.
+
+ The number of vertices is specified in \a indexCount. \a firstIndex is the
+ base index. The effective offset in the index buffer is given by
+ \c{indexOffset + firstIndex * n} where \c n is 2 or 4 depending on the
+ index element type. \c indexOffset is specified in setVertexInput().
+
+ \note The effective offset in the index buffer must be 4 byte aligned with
+ some backends (for example, Metal). With these backends the
+ \l{QRhi::NonFourAlignedEffectiveIndexBufferOffset}{NonFourAlignedEffectiveIndexBufferOffset}
+ feature will be reported as not-supported.
+
+ For instanced drawing set \a instanceCount to a value other than 1. \a
+ firstInstance is the instance ID of the first instance to draw.
+
+ \a vertexOffset is added to the vertex index.
+
+ \note This function can only be called inside a render pass, meaning
+ between a beginPass() and endPass() call.
+ */
+void QRhiCommandBuffer::drawIndexed(quint32 indexCount,
+ quint32 instanceCount, quint32 firstIndex,
+ qint32 vertexOffset, quint32 firstInstance)
+{
+ m_rhi->drawIndexed(this, indexCount, instanceCount, firstIndex, vertexOffset, firstInstance);
+}
+
+/*!
+ Records a named debug group on the command buffer. This is shown in
+ graphics debugging tools such as \l{https://renderdoc.org/}{RenderDoc} and
+ \l{https://developer.apple.com/xcode/}{XCode}. The end of the grouping is
+ indicated by debugMarkEnd().
+
+ \note Ignored when QRhi::DebugMarkers are not supported or
+ QRhi::EnableDebugMarkers is not set.
+
+ \note Can be called anywhere within the frame, both inside and outside of passes.
+ */
+void QRhiCommandBuffer::debugMarkBegin(const QByteArray &name)
+{
+ m_rhi->debugMarkBegin(this, name);
+}
+
+/*!
+ Records the end of a debug group.
+
+ \note Ignored when QRhi::DebugMarkers are not supported or
+ QRhi::EnableDebugMarkers is not set.
+
+ \note Can be called anywhere within the frame, both inside and outside of passes.
+ */
+void QRhiCommandBuffer::debugMarkEnd()
+{
+ m_rhi->debugMarkEnd(this);
+}
+
+/*!
+ Inserts a debug message \a msg into the command stream.
+
+ \note Ignored when QRhi::DebugMarkers are not supported or
+ QRhi::EnableDebugMarkers is not set.
+
+ \note With some backends debugMarkMsg() is only supported inside a pass and
+ is ignored when called outside a pass. With others it is recorded anywhere
+ within the frame.
+ */
+void QRhiCommandBuffer::debugMarkMsg(const QByteArray &msg)
+{
+ m_rhi->debugMarkMsg(this, msg);
+}
+
+/*!
+ Records starting a new compute pass.
+
+ \a resourceUpdates, when not null, specifies a resource update batch that
+ is to be committed and then released.
+
+ \note Do not assume that any state or resource bindings persist between
+ passes.
+
+ \note A compute pass can record setComputePipeline(), setShaderResources(),
+ and dispatch() calls, not graphics ones. General functionality, such as,
+ debug markers and beginExternal() is available both in render and compute
+ passes.
+
+ \note Compute is only available when the \l{QRhi::Compute}{Compute} feature
+ is reported as supported.
+ */
+void QRhiCommandBuffer::beginComputePass(QRhiResourceUpdateBatch *resourceUpdates)
+{
+ m_rhi->beginComputePass(this, resourceUpdates);
+}
+
+/*!
+ Records ending the current compute pass.
+
+ \a resourceUpdates, when not null, specifies a resource update batch that
+ is to be committed and then released.
+ */
+void QRhiCommandBuffer::endComputePass(QRhiResourceUpdateBatch *resourceUpdates)
+{
+ m_rhi->endComputePass(this, resourceUpdates);
+}
+
+/*!
+ Records setting a new compute pipeline \a ps.
+
+ \note This function must be called before recording setShaderResources() or
+ dispatch() commands on the command buffer.
+
+ \note QRhi will optimize out unnecessary invocations within a pass, so
+ therefore overoptimizing to avoid calls to this function is not necessary
+ on the applications' side.
+
+ \note This function can only be called inside a compute pass, meaning
+ between a beginComputePass() and endComputePass() call.
+ */
+void QRhiCommandBuffer::setComputePipeline(QRhiComputePipeline *ps)
+{
+ m_rhi->setComputePipeline(this, ps);
+}
+
+/*!
+ Records dispatching compute work items, with \a x, \a y, and \a z
+ specifying the number of local workgroups in the corresponding dimension.
+
+ \note This function can only be called inside a compute pass, meaning
+ between a beginComputePass() and endComputePass() call.
+ */
+void QRhiCommandBuffer::dispatch(int x, int y, int z)
+{
+ m_rhi->dispatch(this, x, y, z);
+}
+
+/*!
+ \return a pointer to a backend-specific QRhiNativeHandles subclass, such as
+ QRhiVulkanCommandBufferNativeHandles. The returned value is null when
+ exposing the underlying native resources is not supported by, or not
+ applicable to, the backend.
+
+ \sa QRhiVulkanCommandBufferNativeHandles,
+ QRhiMetalCommandBufferNativeHandles, beginExternal(), endExternal()
+ */
+const QRhiNativeHandles *QRhiCommandBuffer::nativeHandles()
+{
+ return m_rhi->nativeHandles(this);
+}
+
+/*!
+ To be called when the application before the application is about to
+ enqueue commands to the current pass' command buffer by calling graphics
+ API functions directly.
+
+ With Vulkan or Metal one can query the native command buffer or encoder
+ objects via nativeHandles() and enqueue commands to them. With OpenGL or
+ Direct3D 11 the (device) context can be retrieved from
+ QRhi::nativeHandles(). However, this must never be done without ensuring
+ the QRhiCommandBuffer's state stays up-to-date. Hence the requirement for
+ wrapping any externally added command recording between beginExternal() and
+ endExternal(). Conceptually this is the same as QPainter's
+ \l{QPainter::beginNativePainting()}{beginNativePainting()} and
+ \l{QPainter::endNativePainting()}{endNativePainting()} functions.
+
+ For OpenGL in particular, this function has an additional task: it makes
+ sure the context is made current on the current thread.
+
+ \note Once beginExternal() is called, no other render pass specific
+ functions (\c set* or \c draw*) must be called on the
+ QRhiCommandBuffer until endExternal().
+
+ \sa endExternal(), nativeHandles()
+ */
+void QRhiCommandBuffer::beginExternal()
+{
+ m_rhi->beginExternal(this);
+}
+
+/*!
+ To be called once the externally added commands are recorded to the command
+ buffer or context.
+
+ \note All QRhiCommandBuffer state must be assumed as invalid after calling
+ this function. Pipelines, vertex and index buffers, and other state must be
+ set again if more draw calls are recorded after the external commands.
+
+ \sa beginExternal(), nativeHandles()
+ */
+void QRhiCommandBuffer::endExternal()
+{
+ m_rhi->endExternal(this);
+}
+
+/*!
+ \return the value (typically an offset) \a v aligned to the uniform buffer
+ alignment given by by ubufAlignment().
+ */
+int QRhi::ubufAligned(int v) const
+{
+ const int byteAlign = ubufAlignment();
+ return (v + byteAlign - 1) & ~(byteAlign - 1);
+}
+
+/*!
+ \return the number of mip levels for a given \a size.
+ */
+int QRhi::mipLevelsForSize(const QSize &size) const
+{
+ return qFloor(std::log2(qMax(size.width(), size.height()))) + 1;
+}
+
+/*!
+ \return the texture image size for a given \a mipLevel, calculated based on
+ the level 0 size given in \a baseLevelSize.
+ */
+QSize QRhi::sizeForMipLevel(int mipLevel, const QSize &baseLevelSize) const
+{
+ const int w = qMax(1, baseLevelSize.width() >> mipLevel);
+ const int h = qMax(1, baseLevelSize.height() >> mipLevel);
+ return QSize(w, h);
+}
+
+/*!
+ \return \c true if the underlying graphics API has the Y axis pointing up
+ in framebuffers and images.
+
+ In practice this is \c true for OpenGL only.
+ */
+bool QRhi::isYUpInFramebuffer() const
+{
+ return d->isYUpInFramebuffer();
+}
+
+/*!
+ \return \c true if the underlying graphics API has the Y axis pointing up
+ in its normalized device coordinate system.
+
+ In practice this is \c false for Vulkan only.
+
+ \note clipSpaceCorrMatrix() includes the corresponding adjustment (to make
+ Y point up) in its returned matrix.
+ */
+bool QRhi::isYUpInNDC() const
+{
+ return d->isYUpInNDC();
+}
+
+/*!
+ \return \c true if the underlying graphics API uses depth 0 - 1 in clip
+ space.
+
+ In practice this is \c false for OpenGL only.
+
+ \note clipSpaceCorrMatrix() includes the corresponding adjustment in its
+ returned matrix.
+ */
+bool QRhi::isClipDepthZeroToOne() const
+{
+ return d->isClipDepthZeroToOne();
+}
+
+/*!
+ \return a matrix that can be used to allow applications keep using
+ OpenGL-targeted vertex data and perspective projection matrices (such as,
+ the ones generated by QMatrix4x4::perspective()), regardless of the
+ backend. Once \c{this_matrix * mvp} is used instead of just \c mvp, vertex
+ data with Y up and viewports with depth range 0 - 1 can be used without
+ considering what backend and so graphics API is going to be used at run
+ time.
+
+ See
+ \l{https://matthewwellings.com/blog/the-new-vulkan-coordinate-system/}{this
+ page} for a discussion of the topic from Vulkan perspective.
+ */
+QMatrix4x4 QRhi::clipSpaceCorrMatrix() const
+{
+ return d->clipSpaceCorrMatrix();
+}
+
+/*!
+ \return \c true if the specified texture \a format modified by \a flags is
+ supported.
+
+ The query is supported both for uncompressed and compressed formats.
+ */
+bool QRhi::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const
+{
+ return d->isTextureFormatSupported(format, flags);
+}
+
+/*!
+ \return \c true if the specified \a feature is supported
+ */
+bool QRhi::isFeatureSupported(QRhi::Feature feature) const
+{
+ return d->isFeatureSupported(feature);
+}
+
+/*!
+ \return the value for the specified resource \a limit.
+
+ The values are expected to be queried by the backends upon initialization,
+ meaning calling this function is a light operation.
+ */
+int QRhi::resourceLimit(ResourceLimit limit) const
+{
+ return d->resourceLimit(limit);
+}
+
+/*!
+ \return a pointer to the backend-specific collection of native objects
+ for the device, context, and similar concepts used by the backend.
+
+ Cast to QRhiVulkanNativeHandles, QRhiD3D11NativeHandles,
+ QRhiGles2NativeHandles, QRhiMetalNativeHandles as appropriate.
+
+ \note No ownership is transferred, neither for the returned pointer nor for
+ any native objects.
+ */
+const QRhiNativeHandles *QRhi::nativeHandles()
+{
+ return d->nativeHandles();
+}
+
+/*!
+ With OpenGL this makes the OpenGL context current on the current thread.
+ The function has no effect with other backends.
+
+ Calling this function is relevant typically in Qt framework code, when one
+ has to ensure external OpenGL code provided by the application can still
+ run like it did before with direct usage of OpenGL, as long as the QRhi is
+ using the OpenGL backend.
+ */
+void QRhi::makeThreadLocalNativeContextCurrent()
+{
+ d->makeThreadLocalNativeContextCurrent();
+}
+
+/*!
+ \return the associated QRhiProfiler instance.
+
+ An instance is always available for each QRhi, but it is not very useful
+ without EnableProfiling because no data is collected without setting the
+ flag upon creation.
+ */
+QRhiProfiler *QRhi::profiler()
+{
+ return &d->profiler;
+}
+
+/*!
+ \return a new graphics pipeline resource.
+
+ \sa QRhiResource::release()
+ */
+QRhiGraphicsPipeline *QRhi::newGraphicsPipeline()
+{
+ return d->createGraphicsPipeline();
+}
+
+/*!
+ \return a new compute pipeline resource.
+
+ \note Compute is only available when the \l{QRhi::Compute}{Compute} feature
+ is reported as supported.
+
+ \sa QRhiResource::release()
+ */
+QRhiComputePipeline *QRhi::newComputePipeline()
+{
+ return d->createComputePipeline();
+}
+
+/*!
+ \return a new shader resource binding collection resource.
+
+ \sa QRhiResource::release()
+ */
+QRhiShaderResourceBindings *QRhi::newShaderResourceBindings()
+{
+ return d->createShaderResourceBindings();
+}
+
+/*!
+ \return a new buffer with the specified \a type, \a usage, and \a size.
+
+ \note Some \a usage and \a type combinations may not be supported by all
+ backends. See \l{QRhiBuffer::UsageFlag}{UsageFlags} and
+ \l{QRhi::NonDynamicUniformBuffers}{the feature flags}.
+
+ \sa QRhiResource::release()
+ */
+QRhiBuffer *QRhi::newBuffer(QRhiBuffer::Type type,
+ QRhiBuffer::UsageFlags usage,
+ int size)
+{
+ return d->createBuffer(type, usage, size);
+}
+
+/*!
+ \return a new renderbuffer with the specified \a type, \a pixelSize, \a
+ sampleCount, and \a flags.
+
+ \sa QRhiResource::release()
+ */
+QRhiRenderBuffer *QRhi::newRenderBuffer(QRhiRenderBuffer::Type type,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiRenderBuffer::Flags flags)
+{
+ return d->createRenderBuffer(type, pixelSize, sampleCount, flags);
+}
+
+/*!
+ \return a new texture with the specified \a format, \a pixelSize, \a
+ sampleCount, and \a flags.
+
+ \note \a format specifies the requested internal and external format,
+ meaning the data to be uploaded to the texture will need to be in a
+ compatible format, while the native texture may (but is not guaranteed to,
+ in case of OpenGL at least) use this format internally.
+
+ \sa QRhiResource::release()
+ */
+QRhiTexture *QRhi::newTexture(QRhiTexture::Format format,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiTexture::Flags flags)
+{
+ return d->createTexture(format, pixelSize, sampleCount, flags);
+}
+
+/*!
+ \return a new sampler with the specified magnification filter \a magFilter,
+ minification filter \a minFilter, mipmapping mode \a mipmapMpde, and S/T
+ addressing modes \a u and \a v.
+
+ \sa QRhiResource::release()
+ */
+QRhiSampler *QRhi::newSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler:: AddressMode u, QRhiSampler::AddressMode v)
+{
+ return d->createSampler(magFilter, minFilter, mipmapMode, u, v);
+}
+
+/*!
+ \return a new texture render target with color and depth/stencil
+ attachments given in \a desc, and with the specified \a flags.
+
+ \sa QRhiResource::release()
+ */
+
+QRhiTextureRenderTarget *QRhi::newTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
+ QRhiTextureRenderTarget::Flags flags)
+{
+ return d->createTextureRenderTarget(desc, flags);
+}
+
+/*!
+ \return a new swapchain.
+
+ \sa QRhiResource::release(), QRhiSwapChain::buildOrResize()
+ */
+QRhiSwapChain *QRhi::newSwapChain()
+{
+ return d->createSwapChain();
+}
+
+/*!
+ Starts a new frame targeting the next available buffer of \a swapChain.
+
+ The high level pattern of rendering into a QWindow using a swapchain:
+
+ \list
+
+ \li Create a swapchain.
+
+ \li Call QRhiSwapChain::buildOrResize() whenever the surface size is
+ different than before.
+
+ \li Call QRhiSwapChain::release() on
+ QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed.
+
+ \li Then on every frame:
+ \badcode
+ beginFrame(sc);
+ updates = nextResourceUpdateBatch();
+ updates->...
+ QRhiCommandBuffer *cb = sc->currentFrameCommandBuffer();
+ cb->beginPass(sc->currentFrameRenderTarget(), colorClear, dsClear, updates);
+ ...
+ cb->endPass();
+ ... // more passes as necessary
+ endFrame(sc);
+ \endcode
+
+ \endlist
+
+ \a flags is currently unused.
+
+ \sa endFrame()
+ */
+QRhi::FrameOpResult QRhi::beginFrame(QRhiSwapChain *swapChain, BeginFrameFlags flags)
+{
+ if (d->inFrame)
+ qWarning("Attempted to call beginFrame() within a still active frame; ignored");
+
+ QRhi::FrameOpResult r = !d->inFrame ? d->beginFrame(swapChain, flags) : FrameOpSuccess;
+ if (r == FrameOpSuccess)
+ d->inFrame = true;
+
+ return r;
+}
+
+/*!
+ Ends, commits, and presents a frame that was started in the last
+ beginFrame() on \a swapChain.
+
+ Double (or triple) buffering is managed internally by the QRhiSwapChain and
+ QRhi.
+
+ \a flags can optionally be used to change the behavior in certain ways.
+ Passing QRhi::SkipPresent skips queuing the Present command or calling
+ swapBuffers.
+
+ \sa beginFrame()
+ */
+QRhi::FrameOpResult QRhi::endFrame(QRhiSwapChain *swapChain, EndFrameFlags flags)
+{
+ if (!d->inFrame)
+ qWarning("Attempted to call endFrame() without an active frame; ignored");
+
+ QRhi::FrameOpResult r = d->inFrame ? d->endFrame(swapChain, flags) : FrameOpSuccess;
+ d->inFrame = false;
+ // releaseAndDestroyLater is a high level QRhi concept the backends know
+ // nothing about - handle it here.
+ qDeleteAll(d->pendingReleaseAndDestroyResources);
+ d->pendingReleaseAndDestroyResources.clear();
+
+ return r;
+}
+
+/*!
+ \return true when there is an active frame, meaning there was a
+ beginFrame() (or beginOffscreenFrame()) with no corresponding endFrame()
+ (or endOffscreenFrame()) yet.
+
+ \sa currentFrameSlot(), beginFrame(), endFrame()
+ */
+bool QRhi::isRecordingFrame() const
+{
+ return d->inFrame;
+}
+
+/*!
+ \return the current frame slot index while recording a frame. Unspecified
+ when called outside an active frame (that is, when isRecordingFrame() is \c
+ false).
+
+ With backends like Vulkan or Metal, it is the responsibility of the QRhi
+ backend to block whenever starting a new frame and finding the CPU is
+ already \c{FramesInFlight - 1} frames ahead of the GPU (because the command
+ buffer submitted in frame no. \c{current} - \c{FramesInFlight} has not yet
+ completed).
+
+ Resources that tend to change between frames (such as, the native buffer
+ object backing a QRhiBuffer with type QRhiBuffer::Dynamic) exist in
+ multiple versions, so that each frame, that can be submitted while a
+ previous one is still being processed, works with its own copy, thus
+ avoiding the need to stall the pipeline when preparing the frame. (The
+ contents of a resource that may still be in use in the GPU should not be
+ touched, but simply always waiting for the previous frame to finish would
+ reduce GPU utilization and ultimately, performance and efficiency.)
+
+ Conceptually this is somewhat similar to copy-on-write schemes used by some
+ C++ containers and other types. It may also be similar to what an OpenGL or
+ Direct 3D 11 implementation performs internally for certain type of objects.
+
+ In practice, such double (or tripple) buffering resources is realized in
+ the Vulkan, Metal, and similar QRhi backends by having a fixed number of
+ native resource (such as, VkBuffer) \c slots behind a QRhiResource. That
+ can then be indexed by a frame slot index running 0, 1, ..,
+ FramesInFlight-1, and then wrapping around.
+
+ All this is managed transparently to the users of QRhi. However,
+ applications that integrate rendering done directly with the graphics API
+ may want to perform a similar double or tripple buffering of their own
+ graphics resources. That is then most easily achieved by knowing the values
+ of the maximum number of in-flight frames (retrievable via resourceLimit())
+ and the current frame (slot) index (returned by this function).
+
+ \sa isRecordingFrame(), beginFrame(), endFrame()
+ */
+int QRhi::currentFrameSlot() const
+{
+ return d->currentFrameSlot;
+}
+
+/*!
+ Starts a new offscreen frame. Provides a command buffer suitable for
+ recording rendering commands in \a cb.
+
+ \note The QRhiCommandBuffer stored to *cb is not owned by the caller.
+
+ Rendering without a swapchain is possible as well. The typical use case is
+ to use it in completely offscreen applications, e.g. to generate image
+ sequences by rendering and reading back without ever showing a window.
+
+ Usage in on-screen applications (so beginFrame, endFrame,
+ beginOffscreenFrame, endOffscreenFrame, beginFrame, ...) is possible too
+ but it does reduce parallelism so it should be done only infrequently.
+
+ Offscreen frames do not let the CPU - potentially - generate another frame
+ while the GPU is still processing the previous one. This has the side
+ effect that if readbacks are scheduled, the results are guaranteed to be
+ available once endOffscreenFrame() returns. That is not the case with
+ frames targeting a swapchain.
+
+ The skeleton of rendering a frame without a swapchain and then reading the
+ frame contents back could look like the following:
+
+ \badcode
+ QRhiReadbackResult rbResult;
+ QRhiCommandBuffer *cb;
+ beginOffscreenFrame(&cb);
+ beginPass
+ ...
+ u = nextResourceUpdateBatch();
+ u->readBackTexture(rb, &rbResult);
+ endPass(u);
+ endOffscreenFrame();
+ // image data available in rbResult
+ \endcode
+
+ \sa endOffscreenFrame()
+ */
+QRhi::FrameOpResult QRhi::beginOffscreenFrame(QRhiCommandBuffer **cb)
+{
+ if (d->inFrame)
+ qWarning("Attempted to call beginOffscreenFrame() within a still active frame; ignored");
+
+ QRhi::FrameOpResult r = !d->inFrame ? d->beginOffscreenFrame(cb) : FrameOpSuccess;
+ if (r == FrameOpSuccess)
+ d->inFrame = true;
+
+ return r;
+}
+
+/*!
+ Ends and waits for the offscreen frame.
+
+ \sa beginOffscreenFrame()
+ */
+QRhi::FrameOpResult QRhi::endOffscreenFrame()
+{
+ if (!d->inFrame)
+ qWarning("Attempted to call endOffscreenFrame() without an active frame; ignored");
+
+ QRhi::FrameOpResult r = d->inFrame ? d->endOffscreenFrame() : FrameOpSuccess;
+ d->inFrame = false;
+ qDeleteAll(d->pendingReleaseAndDestroyResources);
+ d->pendingReleaseAndDestroyResources.clear();
+
+ return r;
+}
+
+/*!
+ Waits for any work on the graphics queue (where applicable) to complete,
+ then executes all deferred operations, like completing readbacks and
+ resource releases. Can be called inside and outside of a frame, but not
+ inside a pass. Inside a frame it implies submitting any work on the
+ command buffer.
+
+ \note Avoid this function. One case where it may be needed is when the
+ results of an enqueued readback in a swapchain-based frame are needed at a
+ fixed given point and so waiting for the results is desired.
+ */
+QRhi::FrameOpResult QRhi::finish()
+{
+ return d->finish();
+}
+
+/*!
+ \return the list of supported sample counts.
+
+ A typical example would be (1, 2, 4, 8).
+
+ With some backend this list of supported values is fixed in advance, while
+ with some others the (physical) device properties indicate what is
+ supported at run time.
+ */
+QVector<int> QRhi::supportedSampleCounts() const
+{
+ return d->supportedSampleCounts();
+}
+
+/*!
+ \return the minimum uniform buffer offset alignment in bytes. This is
+ typically 256.
+
+ Attempting to bind a uniform buffer region with an offset not aligned to
+ this value will lead to failures depending on the backend and the
+ underlying graphics API.
+
+ \sa ubufAligned()
+ */
+int QRhi::ubufAlignment() const
+{
+ return d->ubufAlignment();
+}
+
+QRhiGlobalObjectIdGenerator::Type QRhiGlobalObjectIdGenerator::newId()
+{
+ static QRhiGlobalObjectIdGenerator inst;
+ return ++inst.counter;
+}
+
+bool QRhiPassResourceTracker::isEmpty() const
+{
+ return m_buffers.isEmpty() && m_textures.isEmpty();
+}
+
+void QRhiPassResourceTracker::reset()
+{
+ m_buffers.clear();
+ m_textures.clear();
+}
+
+static inline QRhiPassResourceTracker::BufferStage earlierStage(QRhiPassResourceTracker::BufferStage a,
+ QRhiPassResourceTracker::BufferStage b)
+{
+ return QRhiPassResourceTracker::BufferStage(qMin(int(a), int(b)));
+}
+
+void QRhiPassResourceTracker::registerBuffer(QRhiBuffer *buf, int slot, BufferAccess *access, BufferStage *stage,
+ const UsageState &state)
+{
+ auto it = std::find_if(m_buffers.begin(), m_buffers.end(), [buf](const Buffer &b) { return b.buf == buf; });
+ if (it != m_buffers.end()) {
+ if (it->access != *access) {
+ const QByteArray name = buf->name();
+ qWarning("Buffer %p (%s) used with different accesses within the same pass, this is not allowed.",
+ buf, name.constData());
+ return;
+ }
+ if (it->stage != *stage) {
+ it->stage = earlierStage(it->stage, *stage);
+ *stage = it->stage;
+ }
+ return;
+ }
+
+ Buffer b;
+ b.buf = buf;
+ b.slot = slot;
+ b.access = *access;
+ b.stage = *stage;
+ b.stateAtPassBegin = state; // first use -> initial state
+ m_buffers.append(b);
+}
+
+static inline QRhiPassResourceTracker::TextureStage earlierStage(QRhiPassResourceTracker::TextureStage a,
+ QRhiPassResourceTracker::TextureStage b)
+{
+ return QRhiPassResourceTracker::TextureStage(qMin(int(a), int(b)));
+}
+
+static inline bool isImageLoadStore(QRhiPassResourceTracker::TextureAccess access)
+{
+ return access == QRhiPassResourceTracker::TexStorageLoad
+ || access == QRhiPassResourceTracker::TexStorageStore
+ || access == QRhiPassResourceTracker::TexStorageLoadStore;
+}
+
+void QRhiPassResourceTracker::registerTexture(QRhiTexture *tex, TextureAccess *access, TextureStage *stage,
+ const UsageState &state)
+{
+ auto it = std::find_if(m_textures.begin(), m_textures.end(), [tex](const Texture &t) { return t.tex == tex; });
+ if (it != m_textures.end()) {
+ if (it->access != *access) {
+ // Different subresources of a texture may be used for both load
+ // and store in the same pass. (think reading from one mip level
+ // and writing to another one in a compute shader) This we can
+ // handle by treating the entire resource as read-write.
+ if (isImageLoadStore(it->access) && isImageLoadStore(*access)) {
+ it->access = QRhiPassResourceTracker::TexStorageLoadStore;
+ *access = it->access;
+ } else {
+ const QByteArray name = tex->name();
+ qWarning("Texture %p (%s) used with different accesses within the same pass, this is not allowed.",
+ tex, name.constData());
+ }
+ }
+ if (it->stage != *stage) {
+ it->stage = earlierStage(it->stage, *stage);
+ *stage = it->stage;
+ }
+ return;
+ }
+
+ Texture t;
+ t.tex = tex;
+ t.access = *access;
+ t.stage = *stage;
+ t.stateAtPassBegin = state; // first use -> initial state
+ m_textures.append(t);
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h
new file mode 100644
index 0000000000..0d296d370c
--- /dev/null
+++ b/src/gui/rhi/qrhi_p.h
@@ -0,0 +1,1426 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHI_H
+#define QRHI_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/qtguiglobal.h>
+#include <QSize>
+#include <QMatrix4x4>
+#include <QVector>
+#include <QThread>
+#include <QColor>
+#include <QImage>
+#include <functional>
+#include <array>
+#include <private/qshader_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWindow;
+class QRhiImplementation;
+class QRhiBuffer;
+class QRhiRenderBuffer;
+class QRhiTexture;
+class QRhiSampler;
+class QRhiCommandBuffer;
+class QRhiResourceUpdateBatch;
+class QRhiResourceUpdateBatchPrivate;
+class QRhiProfiler;
+class QRhiShaderResourceBindingPrivate;
+
+class Q_GUI_EXPORT QRhiDepthStencilClearValue
+{
+public:
+ QRhiDepthStencilClearValue() = default;
+ QRhiDepthStencilClearValue(float d, quint32 s);
+
+ float depthClearValue() const { return m_d; }
+ void setDepthClearValue(float d) { m_d = d; }
+
+ quint32 stencilClearValue() const { return m_s; }
+ void setStencilClearValue(quint32 s) { m_s = s; }
+
+private:
+ float m_d = 1.0f;
+ quint32 m_s = 0;
+};
+
+Q_DECLARE_TYPEINFO(QRhiDepthStencilClearValue, Q_MOVABLE_TYPE);
+
+Q_GUI_EXPORT bool operator==(const QRhiDepthStencilClearValue &a, const QRhiDepthStencilClearValue &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator!=(const QRhiDepthStencilClearValue &a, const QRhiDepthStencilClearValue &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT uint qHash(const QRhiDepthStencilClearValue &v, uint seed = 0) Q_DECL_NOTHROW;
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiDepthStencilClearValue &);
+#endif
+
+class Q_GUI_EXPORT QRhiViewport
+{
+public:
+ QRhiViewport() = default;
+ QRhiViewport(float x, float y, float w, float h, float minDepth = 0.0f, float maxDepth = 1.0f);
+
+ std::array<float, 4> viewport() const { return m_rect; }
+ void setViewport(float x, float y, float w, float h) {
+ m_rect[0] = x; m_rect[1] = y; m_rect[2] = w; m_rect[3] = h;
+ }
+
+ float minDepth() const { return m_minDepth; }
+ void setMinDepth(float minDepth) { m_minDepth = minDepth; }
+
+ float maxDepth() const { return m_maxDepth; }
+ void setMaxDepth(float maxDepth) { m_maxDepth = maxDepth; }
+
+private:
+ std::array<float, 4> m_rect { { 0.0f, 0.0f, 0.0f, 0.0f } };
+ float m_minDepth = 0.0f;
+ float m_maxDepth = 1.0f;
+};
+
+Q_DECLARE_TYPEINFO(QRhiViewport, Q_MOVABLE_TYPE);
+
+Q_GUI_EXPORT bool operator==(const QRhiViewport &a, const QRhiViewport &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator!=(const QRhiViewport &a, const QRhiViewport &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT uint qHash(const QRhiViewport &v, uint seed = 0) Q_DECL_NOTHROW;
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiViewport &);
+#endif
+
+class Q_GUI_EXPORT QRhiScissor
+{
+public:
+ QRhiScissor() = default;
+ QRhiScissor(int x, int y, int w, int h);
+
+ std::array<int, 4> scissor() const { return m_rect; }
+ void setScissor(int x, int y, int w, int h) {
+ m_rect[0] = x; m_rect[1] = y; m_rect[2] = w; m_rect[3] = h;
+ }
+
+private:
+ std::array<int, 4> m_rect { { 0, 0, 0, 0 } };
+};
+
+Q_DECLARE_TYPEINFO(QRhiScissor, Q_MOVABLE_TYPE);
+
+Q_GUI_EXPORT bool operator==(const QRhiScissor &a, const QRhiScissor &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator!=(const QRhiScissor &a, const QRhiScissor &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT uint qHash(const QRhiScissor &v, uint seed = 0) Q_DECL_NOTHROW;
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiScissor &);
+#endif
+
+class Q_GUI_EXPORT QRhiVertexInputBinding
+{
+public:
+ enum Classification {
+ PerVertex,
+ PerInstance
+ };
+
+ QRhiVertexInputBinding() = default;
+ QRhiVertexInputBinding(quint32 stride, Classification cls = PerVertex, int stepRate = 1);
+
+ quint32 stride() const { return m_stride; }
+ void setStride(quint32 s) { m_stride = s; }
+
+ Classification classification() const { return m_classification; }
+ void setClassification(Classification c) { m_classification = c; }
+
+ int instanceStepRate() const { return m_instanceStepRate; }
+ void setInstanceStepRate(int rate) { m_instanceStepRate = rate; }
+
+private:
+ quint32 m_stride = 0;
+ Classification m_classification = PerVertex;
+ int m_instanceStepRate = 1;
+};
+
+Q_DECLARE_TYPEINFO(QRhiVertexInputBinding, Q_MOVABLE_TYPE);
+
+Q_GUI_EXPORT bool operator==(const QRhiVertexInputBinding &a, const QRhiVertexInputBinding &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator!=(const QRhiVertexInputBinding &a, const QRhiVertexInputBinding &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT uint qHash(const QRhiVertexInputBinding &v, uint seed = 0) Q_DECL_NOTHROW;
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiVertexInputBinding &);
+#endif
+
+class Q_GUI_EXPORT QRhiVertexInputAttribute
+{
+public:
+ enum Format {
+ Float4,
+ Float3,
+ Float2,
+ Float,
+ UNormByte4,
+ UNormByte2,
+ UNormByte
+ };
+
+ QRhiVertexInputAttribute() = default;
+ QRhiVertexInputAttribute(int binding, int location, Format format, quint32 offset);
+
+ int binding() const { return m_binding; }
+ void setBinding(int b) { m_binding = b; }
+
+ int location() const { return m_location; }
+ void setLocation(int loc) { m_location = loc; }
+
+ Format format() const { return m_format; }
+ void setFormt(Format f) { m_format = f; }
+
+ quint32 offset() const { return m_offset; }
+ void setOffset(quint32 ofs) { m_offset = ofs; }
+
+private:
+ int m_binding = 0;
+ int m_location = 0;
+ Format m_format = Float4;
+ quint32 m_offset = 0;
+};
+
+Q_DECLARE_TYPEINFO(QRhiVertexInputAttribute, Q_MOVABLE_TYPE);
+
+Q_GUI_EXPORT bool operator==(const QRhiVertexInputAttribute &a, const QRhiVertexInputAttribute &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator!=(const QRhiVertexInputAttribute &a, const QRhiVertexInputAttribute &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT uint qHash(const QRhiVertexInputAttribute &v, uint seed = 0) Q_DECL_NOTHROW;
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiVertexInputAttribute &);
+#endif
+
+class Q_GUI_EXPORT QRhiVertexInputLayout
+{
+public:
+ QRhiVertexInputLayout() = default;
+
+ QVector<QRhiVertexInputBinding> bindings() const { return m_bindings; }
+ void setBindings(const QVector<QRhiVertexInputBinding> &v) { m_bindings = v; }
+
+ QVector<QRhiVertexInputAttribute> attributes() const { return m_attributes; }
+ void setAttributes(const QVector<QRhiVertexInputAttribute> &v) { m_attributes = v; }
+
+private:
+ QVector<QRhiVertexInputBinding> m_bindings;
+ QVector<QRhiVertexInputAttribute> m_attributes;
+};
+
+Q_DECLARE_TYPEINFO(QRhiVertexInputLayout, Q_MOVABLE_TYPE);
+
+Q_GUI_EXPORT bool operator==(const QRhiVertexInputLayout &a, const QRhiVertexInputLayout &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator!=(const QRhiVertexInputLayout &a, const QRhiVertexInputLayout &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT uint qHash(const QRhiVertexInputLayout &v, uint seed = 0) Q_DECL_NOTHROW;
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiVertexInputLayout &);
+#endif
+
+class Q_GUI_EXPORT QRhiShaderStage
+{
+public:
+ enum Type {
+ Vertex,
+ Fragment,
+ Compute
+ };
+
+ QRhiShaderStage() = default;
+ QRhiShaderStage(Type type, const QShader &shader,
+ QShader::Variant v = QShader::StandardShader);
+
+ Type type() const { return m_type; }
+ void setType(Type t) { m_type = t; }
+
+ QShader shader() const { return m_shader; }
+ void setShader(const QShader &s) { m_shader = s; }
+
+ QShader::Variant shaderVariant() const { return m_shaderVariant; }
+ void setShaderVariant(QShader::Variant v) { m_shaderVariant = v; }
+
+private:
+ Type m_type = Vertex;
+ QShader m_shader;
+ QShader::Variant m_shaderVariant = QShader::StandardShader;
+};
+
+Q_DECLARE_TYPEINFO(QRhiShaderStage, Q_MOVABLE_TYPE);
+
+Q_GUI_EXPORT bool operator==(const QRhiShaderStage &a, const QRhiShaderStage &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator!=(const QRhiShaderStage &a, const QRhiShaderStage &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT uint qHash(const QRhiShaderStage &s, uint seed = 0) Q_DECL_NOTHROW;
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiShaderStage &);
+#endif
+
+using QRhiGraphicsShaderStage = QRhiShaderStage;
+
+class Q_GUI_EXPORT QRhiShaderResourceBinding
+{
+public:
+ enum Type {
+ UniformBuffer,
+ SampledTexture,
+ ImageLoad,
+ ImageStore,
+ ImageLoadStore,
+ BufferLoad,
+ BufferStore,
+ BufferLoadStore
+ };
+
+ enum StageFlag {
+ VertexStage = 1 << 0,
+ FragmentStage = 1 << 1,
+ ComputeStage = 1 << 2
+ };
+ Q_DECLARE_FLAGS(StageFlags, StageFlag)
+
+ QRhiShaderResourceBinding();
+ QRhiShaderResourceBinding(const QRhiShaderResourceBinding &other);
+ QRhiShaderResourceBinding &operator=(const QRhiShaderResourceBinding &other);
+ ~QRhiShaderResourceBinding();
+ void detach();
+
+ bool isLayoutCompatible(const QRhiShaderResourceBinding &other) const;
+
+ static QRhiShaderResourceBinding uniformBuffer(int binding, StageFlags stage, QRhiBuffer *buf);
+ static QRhiShaderResourceBinding uniformBuffer(int binding, StageFlags stage, QRhiBuffer *buf, int offset, int size);
+ static QRhiShaderResourceBinding uniformBufferWithDynamicOffset(int binding, StageFlags stage, QRhiBuffer *buf, int size);
+
+ static QRhiShaderResourceBinding sampledTexture(int binding, StageFlags stage, QRhiTexture *tex, QRhiSampler *sampler);
+
+ static QRhiShaderResourceBinding imageLoad(int binding, StageFlags stage, QRhiTexture *tex, int level);
+ static QRhiShaderResourceBinding imageStore(int binding, StageFlags stage, QRhiTexture *tex, int level);
+ static QRhiShaderResourceBinding imageLoadStore(int binding, StageFlags stage, QRhiTexture *tex, int level);
+
+ static QRhiShaderResourceBinding bufferLoad(int binding, StageFlags stage, QRhiBuffer *buf);
+ static QRhiShaderResourceBinding bufferLoad(int binding, StageFlags stage, QRhiBuffer *buf, int offset, int size);
+ static QRhiShaderResourceBinding bufferStore(int binding, StageFlags stage, QRhiBuffer *buf);
+ static QRhiShaderResourceBinding bufferStore(int binding, StageFlags stage, QRhiBuffer *buf, int offset, int size);
+ static QRhiShaderResourceBinding bufferLoadStore(int binding, StageFlags stage, QRhiBuffer *buf);
+ static QRhiShaderResourceBinding bufferLoadStore(int binding, StageFlags stage, QRhiBuffer *buf, int offset, int size);
+
+private:
+ QRhiShaderResourceBindingPrivate *d;
+ friend class QRhiShaderResourceBindingPrivate;
+ friend Q_GUI_EXPORT bool operator==(const QRhiShaderResourceBinding &, const QRhiShaderResourceBinding &) Q_DECL_NOTHROW;
+ friend Q_GUI_EXPORT bool operator!=(const QRhiShaderResourceBinding &, const QRhiShaderResourceBinding &) Q_DECL_NOTHROW;
+ friend Q_GUI_EXPORT uint qHash(const QRhiShaderResourceBinding &, uint) Q_DECL_NOTHROW;
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiShaderResourceBinding &);
+#endif
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiShaderResourceBinding::StageFlags)
+
+Q_GUI_EXPORT bool operator==(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator!=(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b) Q_DECL_NOTHROW;
+Q_GUI_EXPORT uint qHash(const QRhiShaderResourceBinding &b, uint seed = 0) Q_DECL_NOTHROW;
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiShaderResourceBinding &);
+#endif
+
+class Q_GUI_EXPORT QRhiColorAttachment
+{
+public:
+ QRhiColorAttachment() = default;
+ QRhiColorAttachment(QRhiTexture *texture);
+ QRhiColorAttachment(QRhiRenderBuffer *renderBuffer);
+
+ QRhiTexture *texture() const { return m_texture; }
+ void setTexture(QRhiTexture *tex) { m_texture = tex; }
+
+ QRhiRenderBuffer *renderBuffer() const { return m_renderBuffer; }
+ void setRenderBuffer(QRhiRenderBuffer *rb) { m_renderBuffer = rb; }
+
+ int layer() const { return m_layer; }
+ void setLayer(int layer) { m_layer = layer; }
+
+ int level() const { return m_level; }
+ void setLevel(int level) { m_level = level; }
+
+ QRhiTexture *resolveTexture() const { return m_resolveTexture; }
+ void setResolveTexture(QRhiTexture *tex) { m_resolveTexture = tex; }
+
+ int resolveLayer() const { return m_resolveLayer; }
+ void setResolveLayer(int layer) { m_resolveLayer = layer; }
+
+ int resolveLevel() const { return m_resolveLevel; }
+ void setResolveLevel(int level) { m_resolveLevel = level; }
+
+private:
+ QRhiTexture *m_texture = nullptr;
+ QRhiRenderBuffer *m_renderBuffer = nullptr;
+ int m_layer = 0;
+ int m_level = 0;
+ QRhiTexture *m_resolveTexture = nullptr;
+ int m_resolveLayer = 0;
+ int m_resolveLevel = 0;
+};
+
+Q_DECLARE_TYPEINFO(QRhiColorAttachment, Q_MOVABLE_TYPE);
+
+class Q_GUI_EXPORT QRhiTextureRenderTargetDescription
+{
+public:
+ QRhiTextureRenderTargetDescription() = default;
+ QRhiTextureRenderTargetDescription(const QRhiColorAttachment &colorAttachment);
+ QRhiTextureRenderTargetDescription(const QRhiColorAttachment &colorAttachment, QRhiRenderBuffer *depthStencilBuffer);
+ QRhiTextureRenderTargetDescription(const QRhiColorAttachment &colorAttachment, QRhiTexture *depthTexture);
+
+ QVector<QRhiColorAttachment> colorAttachments() const { return m_colorAttachments; }
+ void setColorAttachments(const QVector<QRhiColorAttachment> &att) { m_colorAttachments = att; }
+
+ QRhiRenderBuffer *depthStencilBuffer() const { return m_depthStencilBuffer; }
+ void setDepthStencilBuffer(QRhiRenderBuffer *renderBuffer) { m_depthStencilBuffer = renderBuffer; }
+
+ QRhiTexture *depthTexture() const { return m_depthTexture; }
+ void setDepthTexture(QRhiTexture *texture) { m_depthTexture = texture; }
+
+private:
+ QVector<QRhiColorAttachment> m_colorAttachments;
+ QRhiRenderBuffer *m_depthStencilBuffer = nullptr;
+ QRhiTexture *m_depthTexture = nullptr;
+};
+
+Q_DECLARE_TYPEINFO(QRhiTextureRenderTargetDescription, Q_MOVABLE_TYPE);
+
+class Q_GUI_EXPORT QRhiTextureSubresourceUploadDescription
+{
+public:
+ QRhiTextureSubresourceUploadDescription() = default;
+ QRhiTextureSubresourceUploadDescription(const QImage &image);
+ QRhiTextureSubresourceUploadDescription(const void *data, int size);
+
+ QImage image() const { return m_image; }
+ void setImage(const QImage &image) { m_image = image; }
+
+ QByteArray data() const { return m_data; }
+ void setData(const QByteArray &data) { m_data = data; }
+
+ QPoint destinationTopLeft() const { return m_destinationTopLeft; }
+ void setDestinationTopLeft(const QPoint &p) { m_destinationTopLeft = p; }
+
+ QSize sourceSize() const { return m_sourceSize; }
+ void setSourceSize(const QSize &size) { m_sourceSize = size; }
+
+ QPoint sourceTopLeft() const { return m_sourceTopLeft; }
+ void setSourceTopLeft(const QPoint &p) { m_sourceTopLeft = p; }
+
+private:
+ QImage m_image;
+ QByteArray m_data;
+ QPoint m_destinationTopLeft;
+ QSize m_sourceSize;
+ QPoint m_sourceTopLeft;
+};
+
+Q_DECLARE_TYPEINFO(QRhiTextureSubresourceUploadDescription, Q_MOVABLE_TYPE);
+
+class Q_GUI_EXPORT QRhiTextureUploadEntry
+{
+public:
+ QRhiTextureUploadEntry() = default;
+ QRhiTextureUploadEntry(int layer, int level, const QRhiTextureSubresourceUploadDescription &desc);
+
+ int layer() const { return m_layer; }
+ void setLayer(int layer) { m_layer = layer; }
+
+ int level() const { return m_level; }
+ void setLevel(int level) { m_level = level; }
+
+ QRhiTextureSubresourceUploadDescription description() const { return m_desc; }
+ void setDescription(const QRhiTextureSubresourceUploadDescription &desc) { m_desc = desc; }
+
+private:
+ int m_layer = 0;
+ int m_level = 0;
+ QRhiTextureSubresourceUploadDescription m_desc;
+};
+
+Q_DECLARE_TYPEINFO(QRhiTextureUploadEntry, Q_MOVABLE_TYPE);
+
+class Q_GUI_EXPORT QRhiTextureUploadDescription
+{
+public:
+ QRhiTextureUploadDescription() = default;
+ QRhiTextureUploadDescription(const QRhiTextureUploadEntry &entry);
+ QRhiTextureUploadDescription(const QVector<QRhiTextureUploadEntry> &entries);
+
+ QVector<QRhiTextureUploadEntry> entries() const { return m_entries; }
+ void setEntries(const QVector<QRhiTextureUploadEntry> &entries) { m_entries = entries; }
+ void append(const QRhiTextureUploadEntry &entry);
+
+private:
+ QVector<QRhiTextureUploadEntry> m_entries;
+};
+
+Q_DECLARE_TYPEINFO(QRhiTextureUploadDescription, Q_MOVABLE_TYPE);
+
+class Q_GUI_EXPORT QRhiTextureCopyDescription
+{
+public:
+ QRhiTextureCopyDescription() = default;
+
+ QSize pixelSize() const { return m_pixelSize; }
+ void setPixelSize(const QSize &sz) { m_pixelSize = sz; }
+
+ int sourceLayer() const { return m_sourceLayer; }
+ void setSourceLayer(int layer) { m_sourceLayer = layer; }
+
+ int sourceLevel() const { return m_sourceLevel; }
+ void setSourceLevel(int level) { m_sourceLevel = level; }
+
+ QPoint sourceTopLeft() const { return m_sourceTopLeft; }
+ void setSourceTopLeft(const QPoint &p) { m_sourceTopLeft = p; }
+
+ int destinationLayer() const { return m_destinationLayer; }
+ void setDestinationLayer(int layer) { m_destinationLayer = layer; }
+
+ int destinationLevel() const { return m_destinationLevel; }
+ void setDestinationLevel(int level) { m_destinationLevel = level; }
+
+ QPoint destinationTopLeft() const { return m_destinationTopLeft; }
+ void setDestinationTopLeft(const QPoint &p) { m_destinationTopLeft = p; }
+
+private:
+ QSize m_pixelSize;
+ int m_sourceLayer = 0;
+ int m_sourceLevel = 0;
+ QPoint m_sourceTopLeft;
+ int m_destinationLayer = 0;
+ int m_destinationLevel = 0;
+ QPoint m_destinationTopLeft;
+};
+
+Q_DECLARE_TYPEINFO(QRhiTextureCopyDescription, Q_MOVABLE_TYPE);
+
+class Q_GUI_EXPORT QRhiReadbackDescription
+{
+public:
+ QRhiReadbackDescription() = default;
+ QRhiReadbackDescription(QRhiTexture *texture);
+
+ QRhiTexture *texture() const { return m_texture; }
+ void setTexture(QRhiTexture *tex) { m_texture = tex; }
+
+ int layer() const { return m_layer; }
+ void setLayer(int layer) { m_layer = layer; }
+
+ int level() const { return m_level; }
+ void setLevel(int level) { m_level = level; }
+
+private:
+ QRhiTexture *m_texture = nullptr;
+ int m_layer = 0;
+ int m_level = 0;
+};
+
+Q_DECLARE_TYPEINFO(QRhiReadbackDescription, Q_MOVABLE_TYPE);
+
+struct Q_GUI_EXPORT QRhiNativeHandles
+{
+};
+
+class Q_GUI_EXPORT QRhiResource
+{
+public:
+ enum Type {
+ Buffer,
+ Texture,
+ Sampler,
+ RenderBuffer,
+ RenderPassDescriptor,
+ RenderTarget,
+ TextureRenderTarget,
+ ShaderResourceBindings,
+ GraphicsPipeline,
+ SwapChain,
+ ComputePipeline,
+ CommandBuffer
+ };
+
+ virtual ~QRhiResource();
+
+ virtual Type resourceType() const = 0;
+
+ virtual void release() = 0;
+ void releaseAndDestroyLater();
+
+ QByteArray name() const;
+ void setName(const QByteArray &name);
+
+ quint64 globalResourceId() const;
+
+protected:
+ QRhiResource(QRhiImplementation *rhi);
+ Q_DISABLE_COPY(QRhiResource)
+ friend class QRhiImplementation;
+ QRhiImplementation *m_rhi = nullptr;
+ quint64 m_id;
+ QByteArray m_objectName;
+};
+
+class Q_GUI_EXPORT QRhiBuffer : public QRhiResource
+{
+public:
+ enum Type {
+ Immutable,
+ Static,
+ Dynamic
+ };
+
+ enum UsageFlag {
+ VertexBuffer = 1 << 0,
+ IndexBuffer = 1 << 1,
+ UniformBuffer = 1 << 2,
+ StorageBuffer = 1 << 3
+ };
+ Q_DECLARE_FLAGS(UsageFlags, UsageFlag)
+
+ QRhiResource::Type resourceType() const override;
+
+ Type type() const { return m_type; }
+ void setType(Type t) { m_type = t; }
+
+ UsageFlags usage() const { return m_usage; }
+ void setUsage(UsageFlags u) { m_usage = u; }
+
+ int size() const { return m_size; }
+ void setSize(int sz) { m_size = sz; }
+
+ virtual bool build() = 0;
+
+protected:
+ QRhiBuffer(QRhiImplementation *rhi, Type type_, UsageFlags usage_, int size_);
+ Type m_type;
+ UsageFlags m_usage;
+ int m_size;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiBuffer::UsageFlags)
+
+class Q_GUI_EXPORT QRhiTexture : public QRhiResource
+{
+public:
+ enum Flag {
+ RenderTarget = 1 << 0,
+ CubeMap = 1 << 2,
+ MipMapped = 1 << 3,
+ sRGB = 1 << 4,
+ UsedAsTransferSource = 1 << 5,
+ UsedWithGenerateMips = 1 << 6,
+ UsedWithLoadStore = 1 << 7
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ enum Format {
+ UnknownFormat,
+
+ RGBA8,
+ BGRA8,
+ R8,
+ R16,
+ RED_OR_ALPHA8,
+
+ RGBA16F,
+ RGBA32F,
+
+ D16,
+ D32F,
+
+ BC1,
+ BC2,
+ BC3,
+ BC4,
+ BC5,
+ BC6H,
+ BC7,
+
+ ETC2_RGB8,
+ ETC2_RGB8A1,
+ ETC2_RGBA8,
+
+ ASTC_4x4,
+ ASTC_5x4,
+ ASTC_5x5,
+ ASTC_6x5,
+ ASTC_6x6,
+ ASTC_8x5,
+ ASTC_8x6,
+ ASTC_8x8,
+ ASTC_10x5,
+ ASTC_10x6,
+ ASTC_10x8,
+ ASTC_10x10,
+ ASTC_12x10,
+ ASTC_12x12
+ };
+
+ QRhiResource::Type resourceType() const override;
+
+ Format format() const { return m_format; }
+ void setFormat(Format fmt) { m_format = fmt; }
+
+ QSize pixelSize() const { return m_pixelSize; }
+ void setPixelSize(const QSize &sz) { m_pixelSize = sz; }
+
+ Flags flags() const { return m_flags; }
+ void setFlags(Flags f) { m_flags = f; }
+
+ int sampleCount() const { return m_sampleCount; }
+ void setSampleCount(int s) { m_sampleCount = s; }
+
+ virtual bool build() = 0;
+ virtual const QRhiNativeHandles *nativeHandles();
+ virtual bool buildFrom(const QRhiNativeHandles *src);
+
+protected:
+ QRhiTexture(QRhiImplementation *rhi, Format format_, const QSize &pixelSize_,
+ int sampleCount_, Flags flags_);
+ Format m_format;
+ QSize m_pixelSize;
+ int m_sampleCount;
+ Flags m_flags;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiTexture::Flags)
+
+class Q_GUI_EXPORT QRhiSampler : public QRhiResource
+{
+public:
+ enum Filter {
+ None,
+ Nearest,
+ Linear
+ };
+
+ enum AddressMode {
+ Repeat,
+ ClampToEdge,
+ Border,
+ Mirror,
+ MirrorOnce
+ };
+
+ enum CompareOp {
+ Never,
+ Less,
+ Equal,
+ LessOrEqual,
+ Greater,
+ NotEqual,
+ GreaterOrEqual,
+ Always
+ };
+
+ QRhiResource::Type resourceType() const override;
+
+ Filter magFilter() const { return m_magFilter; }
+ void setMagFilter(Filter f) { m_magFilter = f; }
+
+ Filter minFilter() const { return m_minFilter; }
+ void setMinFilter(Filter f) { m_minFilter = f; }
+
+ Filter mipmapMode() const { return m_mipmapMode; }
+ void setMipmapMode(Filter f) { m_mipmapMode = f; }
+
+ AddressMode addressU() const { return m_addressU; }
+ void setAddressU(AddressMode mode) { m_addressU = mode; }
+
+ AddressMode addressV() const { return m_addressV; }
+ void setAddressV(AddressMode mode) { m_addressV = mode; }
+
+ AddressMode addressW() const { return m_addressW; }
+ void setAddressW(AddressMode mode) { m_addressW = mode; }
+
+ CompareOp textureCompareOp() const { return m_compareOp; }
+ void setTextureCompareOp(CompareOp op) { m_compareOp = op; }
+
+ virtual bool build() = 0;
+
+protected:
+ QRhiSampler(QRhiImplementation *rhi,
+ Filter magFilter_, Filter minFilter_, Filter mipmapMode_,
+ AddressMode u_, AddressMode v_);
+ Filter m_magFilter;
+ Filter m_minFilter;
+ Filter m_mipmapMode;
+ AddressMode m_addressU;
+ AddressMode m_addressV;
+ AddressMode m_addressW;
+ CompareOp m_compareOp;
+};
+
+class Q_GUI_EXPORT QRhiRenderBuffer : public QRhiResource
+{
+public:
+ enum Type {
+ DepthStencil,
+ Color
+ };
+
+ enum Flag {
+ UsedWithSwapChainOnly = 1 << 0
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ QRhiResource::Type resourceType() const override;
+
+ Type type() const { return m_type; }
+ void setType(Type t) { m_type = t; }
+
+ QSize pixelSize() const { return m_pixelSize; }
+ void setPixelSize(const QSize &sz) { m_pixelSize = sz; }
+
+ int sampleCount() const { return m_sampleCount; }
+ void setSampleCount(int s) { m_sampleCount = s; }
+
+ Flags flags() const { return m_flags; }
+ void setFlags(Flags h) { m_flags = h; }
+
+ virtual bool build() = 0;
+
+ virtual QRhiTexture::Format backingFormat() const = 0;
+
+protected:
+ QRhiRenderBuffer(QRhiImplementation *rhi, Type type_, const QSize &pixelSize_,
+ int sampleCount_, Flags flags_);
+ Type m_type;
+ QSize m_pixelSize;
+ int m_sampleCount;
+ Flags m_flags;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiRenderBuffer::Flags)
+
+class Q_GUI_EXPORT QRhiRenderPassDescriptor : public QRhiResource
+{
+public:
+ QRhiResource::Type resourceType() const override;
+
+protected:
+ QRhiRenderPassDescriptor(QRhiImplementation *rhi);
+};
+
+class Q_GUI_EXPORT QRhiRenderTarget : public QRhiResource
+{
+public:
+ QRhiResource::Type resourceType() const override;
+
+ virtual QSize pixelSize() const = 0;
+ virtual float devicePixelRatio() const = 0;
+ virtual int sampleCount() const = 0;
+
+ QRhiRenderPassDescriptor *renderPassDescriptor() const { return m_renderPassDesc; }
+ void setRenderPassDescriptor(QRhiRenderPassDescriptor *desc) { m_renderPassDesc = desc; }
+
+protected:
+ QRhiRenderTarget(QRhiImplementation *rhi);
+ QRhiRenderPassDescriptor *m_renderPassDesc = nullptr;
+};
+
+class Q_GUI_EXPORT QRhiTextureRenderTarget : public QRhiRenderTarget
+{
+public:
+ enum Flag {
+ PreserveColorContents = 1 << 0,
+ PreserveDepthStencilContents = 1 << 1
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ QRhiResource::Type resourceType() const override;
+
+ QRhiTextureRenderTargetDescription description() const { return m_desc; }
+ void setDescription(const QRhiTextureRenderTargetDescription &desc) { m_desc = desc; }
+
+ Flags flags() const { return m_flags; }
+ void setFlags(Flags f) { m_flags = f; }
+
+ virtual QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() = 0;
+
+ virtual bool build() = 0;
+
+protected:
+ QRhiTextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc_, Flags flags_);
+ QRhiTextureRenderTargetDescription m_desc;
+ Flags m_flags;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiTextureRenderTarget::Flags)
+
+class Q_GUI_EXPORT QRhiShaderResourceBindings : public QRhiResource
+{
+public:
+ QRhiResource::Type resourceType() const override;
+
+ QVector<QRhiShaderResourceBinding> bindings() const { return m_bindings; }
+ void setBindings(const QVector<QRhiShaderResourceBinding> &b) { m_bindings = b; }
+
+ bool isLayoutCompatible(const QRhiShaderResourceBindings *other) const;
+
+ virtual bool build() = 0;
+
+protected:
+ QRhiShaderResourceBindings(QRhiImplementation *rhi);
+ QVector<QRhiShaderResourceBinding> m_bindings;
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiShaderResourceBindings &);
+#endif
+};
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiShaderResourceBindings &);
+#endif
+
+class Q_GUI_EXPORT QRhiGraphicsPipeline : public QRhiResource
+{
+public:
+ enum Flag {
+ UsesBlendConstants = 1 << 0,
+ UsesStencilRef = 1 << 1,
+ UsesScissor = 1 << 2
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ enum Topology {
+ Triangles,
+ TriangleStrip,
+ Lines,
+ LineStrip,
+ Points
+ };
+
+ enum CullMode {
+ None,
+ Front,
+ Back
+ };
+
+ enum FrontFace {
+ CCW,
+ CW
+ };
+
+ enum ColorMaskComponent {
+ R = 1 << 0,
+ G = 1 << 1,
+ B = 1 << 2,
+ A = 1 << 3
+ };
+ Q_DECLARE_FLAGS(ColorMask, ColorMaskComponent)
+
+ enum BlendFactor {
+ Zero,
+ One,
+ SrcColor,
+ OneMinusSrcColor,
+ DstColor,
+ OneMinusDstColor,
+ SrcAlpha,
+ OneMinusSrcAlpha,
+ DstAlpha,
+ OneMinusDstAlpha,
+ ConstantColor,
+ OneMinusConstantColor,
+ ConstantAlpha,
+ OneMinusConstantAlpha,
+ SrcAlphaSaturate,
+ Src1Color,
+ OneMinusSrc1Color,
+ Src1Alpha,
+ OneMinusSrc1Alpha
+ };
+
+ enum BlendOp {
+ Add,
+ Subtract,
+ ReverseSubtract,
+ Min,
+ Max
+ };
+
+ struct TargetBlend {
+ ColorMask colorWrite = ColorMask(0xF); // R | G | B | A
+ bool enable = false;
+ BlendFactor srcColor = One;
+ BlendFactor dstColor = OneMinusSrcAlpha;
+ BlendOp opColor = Add;
+ BlendFactor srcAlpha = One;
+ BlendFactor dstAlpha = OneMinusSrcAlpha;
+ BlendOp opAlpha = Add;
+ };
+
+ enum CompareOp {
+ Never,
+ Less,
+ Equal,
+ LessOrEqual,
+ Greater,
+ NotEqual,
+ GreaterOrEqual,
+ Always
+ };
+
+ enum StencilOp {
+ StencilZero,
+ Keep,
+ Replace,
+ IncrementAndClamp,
+ DecrementAndClamp,
+ Invert,
+ IncrementAndWrap,
+ DecrementAndWrap
+ };
+
+ struct StencilOpState {
+ StencilOp failOp = Keep;
+ StencilOp depthFailOp = Keep;
+ StencilOp passOp = Keep;
+ CompareOp compareOp = Always;
+ };
+
+ QRhiResource::Type resourceType() const override;
+
+ Flags flags() const { return m_flags; }
+ void setFlags(Flags f) { m_flags = f; }
+
+ Topology topology() const { return m_topology; }
+ void setTopology(Topology t) { m_topology = t; }
+
+ CullMode cullMode() const { return m_cullMode; }
+ void setCullMode(CullMode mode) { m_cullMode = mode; }
+
+ FrontFace frontFace() const { return m_frontFace; }
+ void setFrontFace(FrontFace f) { m_frontFace = f; }
+
+ QVector<TargetBlend> targetBlends() const { return m_targetBlends; }
+ void setTargetBlends(const QVector<TargetBlend> &blends) { m_targetBlends = blends; }
+
+ bool hasDepthTest() const { return m_depthTest; }
+ void setDepthTest(bool enable) { m_depthTest = enable; }
+
+ bool hasDepthWrite() const { return m_depthWrite; }
+ void setDepthWrite(bool enable) { m_depthWrite = enable; }
+
+ CompareOp depthOp() const { return m_depthOp; }
+ void setDepthOp(CompareOp op) { m_depthOp = op; }
+
+ bool hasStencilTest() const { return m_stencilTest; }
+ void setStencilTest(bool enable) { m_stencilTest = enable; }
+
+ StencilOpState stencilFront() const { return m_stencilFront; }
+ void setStencilFront(const StencilOpState &state) { m_stencilFront = state; }
+
+ StencilOpState stencilBack() const { return m_stencilBack; }
+ void setStencilBack(const StencilOpState &state) { m_stencilBack = state; }
+
+ quint32 stencilReadMask() const { return m_stencilReadMask; }
+ void setStencilReadMask(quint32 mask) { m_stencilReadMask = mask; }
+
+ quint32 stencilWriteMask() const { return m_stencilWriteMask; }
+ void setStencilWriteMask(quint32 mask) { m_stencilWriteMask = mask; }
+
+ int sampleCount() const { return m_sampleCount; }
+ void setSampleCount(int s) { m_sampleCount = s; }
+
+ QVector<QRhiShaderStage> shaderStages() const { return m_shaderStages; }
+ void setShaderStages(const QVector<QRhiShaderStage> &stages) { m_shaderStages = stages; }
+
+ QRhiVertexInputLayout vertexInputLayout() const { return m_vertexInputLayout; }
+ void setVertexInputLayout(const QRhiVertexInputLayout &layout) { m_vertexInputLayout = layout; }
+
+ QRhiShaderResourceBindings *shaderResourceBindings() const { return m_shaderResourceBindings; }
+ void setShaderResourceBindings(QRhiShaderResourceBindings *srb) { m_shaderResourceBindings = srb; }
+
+ QRhiRenderPassDescriptor *renderPassDescriptor() const { return m_renderPassDesc; }
+ void setRenderPassDescriptor(QRhiRenderPassDescriptor *desc) { m_renderPassDesc = desc; }
+
+ virtual bool build() = 0;
+
+protected:
+ QRhiGraphicsPipeline(QRhiImplementation *rhi);
+ Flags m_flags;
+ Topology m_topology = Triangles;
+ CullMode m_cullMode = None;
+ FrontFace m_frontFace = CCW;
+ QVector<TargetBlend> m_targetBlends;
+ bool m_depthTest = false;
+ bool m_depthWrite = false;
+ CompareOp m_depthOp = Less;
+ bool m_stencilTest = false;
+ StencilOpState m_stencilFront;
+ StencilOpState m_stencilBack;
+ quint32 m_stencilReadMask = 0xFF;
+ quint32 m_stencilWriteMask = 0xFF;
+ int m_sampleCount = 1;
+ QVector<QRhiShaderStage> m_shaderStages;
+ QRhiVertexInputLayout m_vertexInputLayout;
+ QRhiShaderResourceBindings *m_shaderResourceBindings = nullptr;
+ QRhiRenderPassDescriptor *m_renderPassDesc = nullptr;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiGraphicsPipeline::Flags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiGraphicsPipeline::ColorMask)
+Q_DECLARE_TYPEINFO(QRhiGraphicsPipeline::TargetBlend, Q_MOVABLE_TYPE);
+
+class Q_GUI_EXPORT QRhiSwapChain : public QRhiResource
+{
+public:
+ enum Flag {
+ SurfaceHasPreMulAlpha = 1 << 0,
+ SurfaceHasNonPreMulAlpha = 1 << 1,
+ sRGB = 1 << 2,
+ UsedAsTransferSource = 1 << 3,
+ NoVSync = 1 << 4,
+ MinimalBufferCount = 1 << 5
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ QRhiResource::Type resourceType() const override;
+
+ QWindow *window() const { return m_window; }
+ void setWindow(QWindow *window) { m_window = window; }
+
+ Flags flags() const { return m_flags; }
+ void setFlags(Flags f) { m_flags = f; }
+
+ QRhiRenderBuffer *depthStencil() const { return m_depthStencil; }
+ void setDepthStencil(QRhiRenderBuffer *ds) { m_depthStencil = ds; }
+
+ int sampleCount() const { return m_sampleCount; }
+ void setSampleCount(int samples) { m_sampleCount = samples; }
+
+ QRhiRenderPassDescriptor *renderPassDescriptor() const { return m_renderPassDesc; }
+ void setRenderPassDescriptor(QRhiRenderPassDescriptor *desc) { m_renderPassDesc = desc; }
+
+ QSize currentPixelSize() const { return m_currentPixelSize; }
+
+ virtual QRhiCommandBuffer *currentFrameCommandBuffer() = 0;
+ virtual QRhiRenderTarget *currentFrameRenderTarget() = 0;
+ virtual QSize surfacePixelSize() = 0;
+ virtual QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() = 0;
+ virtual bool buildOrResize() = 0;
+
+protected:
+ QRhiSwapChain(QRhiImplementation *rhi);
+ QWindow *m_window = nullptr;
+ Flags m_flags;
+ QRhiRenderBuffer *m_depthStencil = nullptr;
+ int m_sampleCount = 1;
+ QRhiRenderPassDescriptor *m_renderPassDesc = nullptr;
+ QSize m_currentPixelSize;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiSwapChain::Flags)
+
+class Q_GUI_EXPORT QRhiComputePipeline : public QRhiResource
+{
+public:
+ QRhiResource::Type resourceType() const override;
+ virtual bool build() = 0;
+
+ QRhiShaderStage shaderStage() const { return m_shaderStage; }
+ void setShaderStage(const QRhiShaderStage &stage) { m_shaderStage = stage; }
+
+ QRhiShaderResourceBindings *shaderResourceBindings() const { return m_shaderResourceBindings; }
+ void setShaderResourceBindings(QRhiShaderResourceBindings *srb) { m_shaderResourceBindings = srb; }
+
+protected:
+ QRhiComputePipeline(QRhiImplementation *rhi);
+ QRhiShaderStage m_shaderStage;
+ QRhiShaderResourceBindings *m_shaderResourceBindings = nullptr;
+};
+
+class Q_GUI_EXPORT QRhiCommandBuffer : public QRhiResource
+{
+public:
+ enum IndexFormat {
+ IndexUInt16,
+ IndexUInt32
+ };
+
+ QRhiResource::Type resourceType() const override;
+
+ void resourceUpdate(QRhiResourceUpdateBatch *resourceUpdates);
+
+ void beginPass(QRhiRenderTarget *rt,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ QRhiResourceUpdateBatch *resourceUpdates = nullptr);
+ void endPass(QRhiResourceUpdateBatch *resourceUpdates = nullptr);
+
+ void setGraphicsPipeline(QRhiGraphicsPipeline *ps);
+ using DynamicOffset = QPair<int, quint32>; // binding, offset
+ void setShaderResources(QRhiShaderResourceBindings *srb = nullptr,
+ int dynamicOffsetCount = 0,
+ const DynamicOffset *dynamicOffsets = nullptr);
+ using VertexInput = QPair<QRhiBuffer *, quint32>; // buffer, offset
+ void setVertexInput(int startBinding, int bindingCount, const VertexInput *bindings,
+ QRhiBuffer *indexBuf = nullptr, quint32 indexOffset = 0,
+ IndexFormat indexFormat = IndexUInt16);
+
+ void setViewport(const QRhiViewport &viewport);
+ void setScissor(const QRhiScissor &scissor);
+ void setBlendConstants(const QColor &c);
+ void setStencilRef(quint32 refValue);
+
+ void draw(quint32 vertexCount,
+ quint32 instanceCount = 1,
+ quint32 firstVertex = 0,
+ quint32 firstInstance = 0);
+
+ void drawIndexed(quint32 indexCount,
+ quint32 instanceCount = 1,
+ quint32 firstIndex = 0,
+ qint32 vertexOffset = 0,
+ quint32 firstInstance = 0);
+
+ void debugMarkBegin(const QByteArray &name);
+ void debugMarkEnd();
+ void debugMarkMsg(const QByteArray &msg);
+
+ void beginComputePass(QRhiResourceUpdateBatch *resourceUpdates = nullptr);
+ void endComputePass(QRhiResourceUpdateBatch *resourceUpdates = nullptr);
+ void setComputePipeline(QRhiComputePipeline *ps);
+ void dispatch(int x, int y, int z);
+
+ const QRhiNativeHandles *nativeHandles();
+ void beginExternal();
+ void endExternal();
+
+protected:
+ QRhiCommandBuffer(QRhiImplementation *rhi);
+};
+
+struct Q_GUI_EXPORT QRhiReadbackResult
+{
+ std::function<void()> completed = nullptr;
+ QRhiTexture::Format format;
+ QSize pixelSize;
+ QByteArray data;
+}; // non-movable due to the std::function
+
+class Q_GUI_EXPORT QRhiResourceUpdateBatch
+{
+public:
+ ~QRhiResourceUpdateBatch();
+
+ void release();
+
+ void merge(QRhiResourceUpdateBatch *other);
+
+ void updateDynamicBuffer(QRhiBuffer *buf, int offset, int size, const void *data);
+ void uploadStaticBuffer(QRhiBuffer *buf, int offset, int size, const void *data);
+ void uploadStaticBuffer(QRhiBuffer *buf, const void *data);
+ void uploadTexture(QRhiTexture *tex, const QRhiTextureUploadDescription &desc);
+ void uploadTexture(QRhiTexture *tex, const QImage &image);
+ void copyTexture(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc = QRhiTextureCopyDescription());
+ void readBackTexture(const QRhiReadbackDescription &rb, QRhiReadbackResult *result);
+ void generateMips(QRhiTexture *tex, int layer = 0);
+
+private:
+ QRhiResourceUpdateBatch(QRhiImplementation *rhi);
+ Q_DISABLE_COPY(QRhiResourceUpdateBatch)
+ QRhiResourceUpdateBatchPrivate *d;
+ friend class QRhiResourceUpdateBatchPrivate;
+ friend class QRhi;
+};
+
+struct Q_GUI_EXPORT QRhiInitParams
+{
+};
+
+class Q_GUI_EXPORT QRhi
+{
+public:
+ enum Implementation {
+ Null,
+ Vulkan,
+ OpenGLES2,
+ D3D11,
+ Metal
+ };
+
+ enum Flag {
+ EnableProfiling = 1 << 0,
+ EnableDebugMarkers = 1 << 1
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ enum FrameOpResult {
+ FrameOpSuccess = 0,
+ FrameOpError,
+ FrameOpSwapChainOutOfDate,
+ FrameOpDeviceLost
+ };
+
+ enum Feature {
+ MultisampleTexture = 1,
+ MultisampleRenderBuffer,
+ DebugMarkers,
+ Timestamps,
+ Instancing,
+ CustomInstanceStepRate,
+ PrimitiveRestart,
+ NonDynamicUniformBuffers,
+ NonFourAlignedEffectiveIndexBufferOffset,
+ NPOTTextureRepeat,
+ RedOrAlpha8IsRed,
+ ElementIndexUint,
+ Compute
+ };
+
+ enum BeginFrameFlag {
+ };
+ Q_DECLARE_FLAGS(BeginFrameFlags, BeginFrameFlag)
+
+ enum EndFrameFlag {
+ SkipPresent = 1 << 0
+ };
+ Q_DECLARE_FLAGS(EndFrameFlags, EndFrameFlag)
+
+ enum ResourceLimit {
+ TextureSizeMin = 1,
+ TextureSizeMax,
+ MaxColorAttachments,
+ FramesInFlight
+ };
+
+ ~QRhi();
+
+ static QRhi *create(Implementation impl,
+ QRhiInitParams *params,
+ Flags flags = Flags(),
+ QRhiNativeHandles *importDevice = nullptr);
+
+ Implementation backend() const;
+ QThread *thread() const;
+
+ using CleanupCallback = std::function<void(QRhi *)>;
+ void addCleanupCallback(const CleanupCallback &callback);
+ void runCleanup();
+
+ QRhiGraphicsPipeline *newGraphicsPipeline();
+ QRhiComputePipeline *newComputePipeline();
+ QRhiShaderResourceBindings *newShaderResourceBindings();
+
+ QRhiBuffer *newBuffer(QRhiBuffer::Type type,
+ QRhiBuffer::UsageFlags usage,
+ int size);
+
+ QRhiRenderBuffer *newRenderBuffer(QRhiRenderBuffer::Type type,
+ const QSize &pixelSize,
+ int sampleCount = 1,
+ QRhiRenderBuffer::Flags flags = QRhiRenderBuffer::Flags());
+
+ QRhiTexture *newTexture(QRhiTexture::Format format,
+ const QSize &pixelSize,
+ int sampleCount = 1,
+ QRhiTexture::Flags flags = QRhiTexture::Flags());
+
+ QRhiSampler *newSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler::AddressMode u, QRhiSampler::AddressMode v);
+
+ QRhiTextureRenderTarget *newTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
+ QRhiTextureRenderTarget::Flags flags = QRhiTextureRenderTarget::Flags());
+
+ QRhiSwapChain *newSwapChain();
+ FrameOpResult beginFrame(QRhiSwapChain *swapChain, BeginFrameFlags flags = BeginFrameFlags());
+ FrameOpResult endFrame(QRhiSwapChain *swapChain, EndFrameFlags flags = EndFrameFlags());
+ bool isRecordingFrame() const;
+ int currentFrameSlot() const;
+
+ FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb);
+ FrameOpResult endOffscreenFrame();
+
+ QRhi::FrameOpResult finish();
+
+ QRhiResourceUpdateBatch *nextResourceUpdateBatch();
+
+ QVector<int> supportedSampleCounts() const;
+
+ int ubufAlignment() const;
+ int ubufAligned(int v) const;
+
+ int mipLevelsForSize(const QSize &size) const;
+ QSize sizeForMipLevel(int mipLevel, const QSize &baseLevelSize) const;
+
+ bool isYUpInFramebuffer() const;
+ bool isYUpInNDC() const;
+ bool isClipDepthZeroToOne() const;
+
+ QMatrix4x4 clipSpaceCorrMatrix() const;
+
+ bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags = QRhiTexture::Flags()) const;
+ bool isFeatureSupported(QRhi::Feature feature) const;
+ int resourceLimit(ResourceLimit limit) const;
+
+ const QRhiNativeHandles *nativeHandles();
+ void makeThreadLocalNativeContextCurrent();
+
+ QRhiProfiler *profiler();
+
+ static const int MAX_LAYERS = 6; // cubemaps only
+ static const int MAX_LEVELS = 16; // a width and/or height of 65536 should be enough for everyone
+
+protected:
+ QRhi();
+
+private:
+ Q_DISABLE_COPY(QRhi)
+ QRhiImplementation *d = nullptr;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QRhi::Flags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QRhi::BeginFrameFlags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QRhi::EndFrameFlags)
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhi_p_p.h b/src/gui/rhi/qrhi_p_p.h
new file mode 100644
index 0000000000..4fd01d3ef2
--- /dev/null
+++ b/src/gui/rhi/qrhi_p_p.h
@@ -0,0 +1,570 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHI_P_H
+#define QRHI_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qrhi_p.h"
+#include "qrhiprofiler_p_p.h"
+#include <QBitArray>
+#include <QAtomicInt>
+#include <QAtomicInteger>
+
+QT_BEGIN_NAMESPACE
+
+#define QRHI_RES(t, x) static_cast<t *>(x)
+#define QRHI_RES_RHI(t) t *rhiD = static_cast<t *>(m_rhi)
+#define QRHI_PROF QRhiProfilerPrivate *rhiP = m_rhi->profilerPrivateOrNull()
+#define QRHI_PROF_F(f) for (bool qrhip_enabled = rhiP != nullptr; qrhip_enabled; qrhip_enabled = false) rhiP->f
+
+class QRhiImplementation
+{
+public:
+ virtual ~QRhiImplementation();
+
+ virtual bool create(QRhi::Flags flags) = 0;
+ virtual void destroy() = 0;
+
+ virtual QRhiGraphicsPipeline *createGraphicsPipeline() = 0;
+ virtual QRhiComputePipeline *createComputePipeline() = 0;
+ virtual QRhiShaderResourceBindings *createShaderResourceBindings() = 0;
+ virtual QRhiBuffer *createBuffer(QRhiBuffer::Type type,
+ QRhiBuffer::UsageFlags usage,
+ int size) = 0;
+ virtual QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiRenderBuffer::Flags flags) = 0;
+ virtual QRhiTexture *createTexture(QRhiTexture::Format format,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiTexture::Flags flags) = 0;
+ virtual QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler:: AddressMode u, QRhiSampler::AddressMode v) = 0;
+
+ virtual QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
+ QRhiTextureRenderTarget::Flags flags) = 0;
+
+ virtual QRhiSwapChain *createSwapChain() = 0;
+ virtual QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) = 0;
+ virtual QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) = 0;
+ virtual QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) = 0;
+ virtual QRhi::FrameOpResult endOffscreenFrame() = 0;
+ virtual QRhi::FrameOpResult finish() = 0;
+
+ virtual void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0;
+
+ virtual void beginPass(QRhiCommandBuffer *cb,
+ QRhiRenderTarget *rt,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ QRhiResourceUpdateBatch *resourceUpdates) = 0;
+ virtual void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0;
+
+ virtual void setGraphicsPipeline(QRhiCommandBuffer *cb,
+ QRhiGraphicsPipeline *ps) = 0;
+
+ virtual void setShaderResources(QRhiCommandBuffer *cb,
+ QRhiShaderResourceBindings *srb,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) = 0;
+
+ virtual void setVertexInput(QRhiCommandBuffer *cb,
+ int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
+ QRhiBuffer *indexBuf, quint32 indexOffset,
+ QRhiCommandBuffer::IndexFormat indexFormat) = 0;
+
+ virtual void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) = 0;
+ virtual void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) = 0;
+ virtual void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) = 0;
+ virtual void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) = 0;
+
+ virtual void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
+ quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) = 0;
+ virtual void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
+ quint32 instanceCount, quint32 firstIndex,
+ qint32 vertexOffset, quint32 firstInstance) = 0;
+
+ virtual void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) = 0;
+ virtual void debugMarkEnd(QRhiCommandBuffer *cb) = 0;
+ virtual void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) = 0;
+
+ virtual void beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0;
+ virtual void endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0;
+ virtual void setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) = 0;
+ virtual void dispatch(QRhiCommandBuffer *cb, int x, int y, int z) = 0;
+
+ virtual const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) = 0;
+ virtual void beginExternal(QRhiCommandBuffer *cb) = 0;
+ virtual void endExternal(QRhiCommandBuffer *cb) = 0;
+
+ virtual QVector<int> supportedSampleCounts() const = 0;
+ virtual int ubufAlignment() const = 0;
+ virtual bool isYUpInFramebuffer() const = 0;
+ virtual bool isYUpInNDC() const = 0;
+ virtual bool isClipDepthZeroToOne() const = 0;
+ virtual QMatrix4x4 clipSpaceCorrMatrix() const = 0;
+ virtual bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const = 0;
+ virtual bool isFeatureSupported(QRhi::Feature feature) const = 0;
+ virtual int resourceLimit(QRhi::ResourceLimit limit) const = 0;
+ virtual const QRhiNativeHandles *nativeHandles() = 0;
+ virtual void sendVMemStatsToProfiler() = 0;
+ virtual void makeThreadLocalNativeContextCurrent() = 0;
+
+ bool isCompressedFormat(QRhiTexture::Format format) const;
+ void compressedFormatInfo(QRhiTexture::Format format, const QSize &size,
+ quint32 *bpl, quint32 *byteSize,
+ QSize *blockDim) const;
+ void textureFormatInfo(QRhiTexture::Format format, const QSize &size,
+ quint32 *bpl, quint32 *byteSize) const;
+ quint32 approxByteSizeForTexture(QRhiTexture::Format format, const QSize &baseSize,
+ int mipCount, int layerCount);
+
+ QRhiProfilerPrivate *profilerPrivateOrNull()
+ {
+ // return null when QRhi::EnableProfiling was not set
+ QRhiProfilerPrivate *p = QRhiProfilerPrivate::get(&profiler);
+ return p->rhiDWhenEnabled ? p : nullptr;
+ }
+
+ // only really care about resources that own native graphics resources underneath
+ void registerResource(QRhiResource *res)
+ {
+ resources.insert(res);
+ }
+
+ void unregisterResource(QRhiResource *res)
+ {
+ resources.remove(res);
+ }
+
+ QSet<QRhiResource *> activeResources() const
+ {
+ return resources;
+ }
+
+ void addReleaseAndDestroyLater(QRhiResource *res)
+ {
+ if (inFrame)
+ pendingReleaseAndDestroyResources.insert(res);
+ else
+ delete res;
+ }
+
+ void addCleanupCallback(const QRhi::CleanupCallback &callback)
+ {
+ cleanupCallbacks.append(callback);
+ }
+
+ QRhi *q;
+
+protected:
+ bool debugMarkers = false;
+ int currentFrameSlot = 0; // for vk, mtl, and similar. unused by gl and d3d11.
+ bool inFrame = false;
+
+private:
+ QRhi::Implementation implType;
+ QThread *implThread;
+ QRhiProfiler profiler;
+ QVector<QRhiResourceUpdateBatch *> resUpdPool;
+ QBitArray resUpdPoolMap;
+ QSet<QRhiResource *> resources;
+ QSet<QRhiResource *> pendingReleaseAndDestroyResources;
+ QVector<QRhi::CleanupCallback> cleanupCallbacks;
+
+ friend class QRhi;
+ friend class QRhiResourceUpdateBatchPrivate;
+};
+
+template<typename T, size_t N>
+bool qrhi_toTopLeftRenderTargetRect(const QSize &outputSize, const std::array<T, N> &r,
+ T *x, T *y, T *w, T *h)
+{
+ // x,y are bottom-left in QRhiScissor and QRhiViewport but top-left in
+ // Vulkan/Metal/D3D. We also need proper clamping since some
+ // validation/debug layers are allergic to out of bounds scissor or
+ // viewport rects.
+
+ const T outputWidth = outputSize.width();
+ const T outputHeight = outputSize.height();
+ const T inputWidth = r[2];
+ const T inputHeight = r[3];
+
+ *x = qMax<T>(0, r[0]);
+ *y = qMax<T>(0, outputHeight - (r[1] + inputHeight));
+ *w = inputWidth;
+ *h = inputHeight;
+
+ if (*x >= outputWidth || *y >= outputHeight)
+ return false;
+
+ if (*x + *w > outputWidth)
+ *w = outputWidth - *x;
+ if (*y + *h > outputHeight)
+ *h = outputHeight - *y;
+
+ return true;
+}
+
+class QRhiResourceUpdateBatchPrivate
+{
+public:
+ struct DynamicBufferUpdate {
+ DynamicBufferUpdate() { }
+ DynamicBufferUpdate(QRhiBuffer *buf_, int offset_, int size_, const void *data_)
+ : buf(buf_), offset(offset_), data(reinterpret_cast<const char *>(data_), size_)
+ { }
+
+ QRhiBuffer *buf = nullptr;
+ int offset = 0;
+ QByteArray data;
+ };
+
+ struct StaticBufferUpload {
+ StaticBufferUpload() { }
+ StaticBufferUpload(QRhiBuffer *buf_, int offset_, int size_, const void *data_)
+ : buf(buf_), offset(offset_), data(reinterpret_cast<const char *>(data_), size_ ? size_ : buf_->size())
+ { }
+
+ QRhiBuffer *buf = nullptr;
+ int offset = 0;
+ QByteArray data;
+ };
+
+ struct TextureOp {
+ enum Type {
+ Upload,
+ Copy,
+ Read,
+ MipGen
+ };
+ Type type;
+ struct SUpload {
+ QRhiTexture *tex = nullptr;
+ // Specifying multiple uploads for a subresource must be supported.
+ // In the backend this can then end up, where applicable, as a
+ // single, batched copy operation with only one set of barriers.
+ // This helps when doing for example glyph cache fills.
+ QVector<QRhiTextureSubresourceUploadDescription> subresDesc[QRhi::MAX_LAYERS][QRhi::MAX_LEVELS];
+ } upload;
+ struct SCopy {
+ QRhiTexture *dst = nullptr;
+ QRhiTexture *src = nullptr;
+ QRhiTextureCopyDescription desc;
+ } copy;
+ struct SRead {
+ QRhiReadbackDescription rb;
+ QRhiReadbackResult *result;
+ } read;
+ struct SMipGen {
+ QRhiTexture *tex = nullptr;
+ int layer = 0;
+ } mipgen;
+
+ static TextureOp textureUpload(QRhiTexture *tex, const QRhiTextureUploadDescription &desc)
+ {
+ TextureOp op;
+ op.type = Upload;
+ op.upload.tex = tex;
+ const QVector<QRhiTextureUploadEntry> &entries(desc.entries());
+ for (const QRhiTextureUploadEntry &entry : entries)
+ op.upload.subresDesc[entry.layer()][entry.level()].append(entry.description());
+ return op;
+ }
+
+ static TextureOp textureCopy(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc)
+ {
+ TextureOp op;
+ op.type = Copy;
+ op.copy.dst = dst;
+ op.copy.src = src;
+ op.copy.desc = desc;
+ return op;
+ }
+
+ static TextureOp textureRead(const QRhiReadbackDescription &rb, QRhiReadbackResult *result)
+ {
+ TextureOp op;
+ op.type = Read;
+ op.read.rb = rb;
+ op.read.result = result;
+ return op;
+ }
+
+ static TextureOp textureMipGen(QRhiTexture *tex, int layer)
+ {
+ TextureOp op;
+ op.type = MipGen;
+ op.mipgen.tex = tex;
+ op.mipgen.layer = layer;
+ return op;
+ }
+ };
+
+ QVector<DynamicBufferUpdate> dynamicBufferUpdates;
+ QVector<StaticBufferUpload> staticBufferUploads;
+ QVector<TextureOp> textureOps;
+
+ QRhiResourceUpdateBatch *q = nullptr;
+ QRhiImplementation *rhi = nullptr;
+ int poolIndex = -1;
+
+ void free();
+ void merge(QRhiResourceUpdateBatchPrivate *other);
+
+ static QRhiResourceUpdateBatchPrivate *get(QRhiResourceUpdateBatch *b) { return b->d; }
+};
+
+Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::StaticBufferUpload, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QRhiResourceUpdateBatchPrivate::TextureOp, Q_MOVABLE_TYPE);
+
+class Q_GUI_EXPORT QRhiShaderResourceBindingPrivate
+{
+public:
+ QRhiShaderResourceBindingPrivate()
+ : ref(1)
+ {
+ }
+
+ QRhiShaderResourceBindingPrivate(const QRhiShaderResourceBindingPrivate *other)
+ : ref(1),
+ binding(other->binding),
+ stage(other->stage),
+ type(other->type),
+ u(other->u)
+ {
+ }
+
+ static QRhiShaderResourceBindingPrivate *get(QRhiShaderResourceBinding *s) { return s->d; }
+ static const QRhiShaderResourceBindingPrivate *get(const QRhiShaderResourceBinding *s) { return s->d; }
+
+ QAtomicInt ref;
+ int binding;
+ QRhiShaderResourceBinding::StageFlags stage;
+ QRhiShaderResourceBinding::Type type;
+ struct UniformBufferData {
+ QRhiBuffer *buf;
+ int offset;
+ int maybeSize;
+ bool hasDynamicOffset;
+ };
+ struct SampledTextureData {
+ QRhiTexture *tex;
+ QRhiSampler *sampler;
+ };
+ struct StorageImageData {
+ QRhiTexture *tex;
+ int level;
+ };
+ struct StorageBufferData {
+ QRhiBuffer *buf;
+ int offset;
+ int maybeSize;
+ };
+ union {
+ UniformBufferData ubuf;
+ SampledTextureData stex;
+ StorageImageData simage;
+ StorageBufferData sbuf;
+ } u;
+};
+
+template<typename T>
+struct QRhiBatchedBindings
+{
+ void feed(int binding, T resource) { // binding must be strictly increasing
+ if (curBinding == -1 || binding > curBinding + 1) {
+ finish();
+ curBatch.startBinding = binding;
+ curBatch.resources.clear();
+ curBatch.resources.append(resource);
+ } else {
+ Q_ASSERT(binding == curBinding + 1);
+ curBatch.resources.append(resource);
+ }
+ curBinding = binding;
+ }
+
+ void finish() {
+ if (!curBatch.resources.isEmpty())
+ batches.append(curBatch);
+ }
+
+ void clear() {
+ batches.clear();
+ curBatch.resources.clear();
+ curBinding = -1;
+ }
+
+ struct Batch {
+ uint startBinding;
+ QVarLengthArray<T, 4> resources;
+
+ bool operator==(const Batch &other) const
+ {
+ return startBinding == other.startBinding && resources == other.resources;
+ }
+
+ bool operator!=(const Batch &other) const
+ {
+ return !operator==(other);
+ }
+ };
+
+ QVarLengthArray<Batch, 4> batches; // sorted by startBinding
+
+ bool operator==(const QRhiBatchedBindings<T> &other) const
+ {
+ return batches == other.batches;
+ }
+
+ bool operator!=(const QRhiBatchedBindings<T> &other) const
+ {
+ return !operator==(other);
+ }
+
+private:
+ Batch curBatch;
+ int curBinding = -1;
+};
+
+class QRhiGlobalObjectIdGenerator
+{
+public:
+#ifdef Q_ATOMIC_INT64_IS_SUPPORTED
+ using Type = quint64;
+#else
+ using Type = quint32;
+#endif
+ static Type newId();
+
+private:
+ QAtomicInteger<Type> counter;
+};
+
+class QRhiPassResourceTracker
+{
+public:
+ bool isEmpty() const;
+ void reset();
+
+ struct UsageState {
+ int layout;
+ int access;
+ int stage;
+ };
+
+ enum BufferStage {
+ BufVertexInputStage,
+ BufVertexStage,
+ BufFragmentStage,
+ BufComputeStage
+ };
+
+ enum BufferAccess {
+ BufVertexInput,
+ BufIndexRead,
+ BufUniformRead,
+ BufStorageLoad,
+ BufStorageStore,
+ BufStorageLoadStore
+ };
+
+ void registerBuffer(QRhiBuffer *buf, int slot, BufferAccess *access, BufferStage *stage,
+ const UsageState &state);
+
+ enum TextureStage {
+ TexVertexStage,
+ TexFragmentStage,
+ TexColorOutputStage,
+ TexDepthOutputStage,
+ TexComputeStage
+ };
+
+ enum TextureAccess {
+ TexSample,
+ TexColorOutput,
+ TexDepthOutput,
+ TexStorageLoad,
+ TexStorageStore,
+ TexStorageLoadStore
+ };
+
+ void registerTexture(QRhiTexture *tex, TextureAccess *access, TextureStage *stage,
+ const UsageState &state);
+
+ struct Buffer {
+ QRhiBuffer *buf;
+ int slot;
+ BufferAccess access;
+ BufferStage stage;
+ UsageState stateAtPassBegin;
+ };
+ const QVector<Buffer> *buffers() const { return &m_buffers; }
+
+ struct Texture {
+ QRhiTexture *tex;
+ TextureAccess access;
+ TextureStage stage;
+ UsageState stateAtPassBegin;
+ };
+ const QVector<Texture> *textures() const { return &m_textures; }
+
+private:
+ QVector<Buffer> m_buffers;
+ QVector<Texture> m_textures;
+};
+
+Q_DECLARE_TYPEINFO(QRhiPassResourceTracker::Buffer, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QRhiPassResourceTracker::Texture, Q_MOVABLE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp
new file mode 100644
index 0000000000..7d9c934c18
--- /dev/null
+++ b/src/gui/rhi/qrhid3d11.cpp
@@ -0,0 +1,3771 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qrhid3d11_p_p.h"
+#include "qshader_p.h"
+#include <QWindow>
+#include <QOperatingSystemVersion>
+#include <qmath.h>
+
+#include <d3dcompiler.h>
+#include <comdef.h>
+
+QT_BEGIN_NAMESPACE
+
+/*
+ Direct3D 11 backend. Provides a double-buffered flip model (FLIP_DISCARD)
+ swapchain. Textures and "static" buffers are USAGE_DEFAULT, leaving it to
+ UpdateSubResource to upload the data in any way it sees fit. "Dynamic"
+ buffers are USAGE_DYNAMIC and updating is done by mapping with WRITE_DISCARD.
+ (so here QRhiBuffer keeps a copy of the buffer contents and all of it is
+ memcpy'd every time, leaving the rest (juggling with the memory area Map
+ returns) to the driver).
+*/
+
+/*!
+ \class QRhiD3D11InitParams
+ \inmodule QtRhi
+ \brief Direct3D 11 specific initialization parameters.
+
+ A D3D11-based QRhi needs no special parameters for initialization. If
+ desired, enableDebugLayer can be set to \c true to enable the Direct3D
+ debug layer. This can be useful during development, but should be avoided
+ in production builds.
+
+ \badcode
+ QRhiD3D11InitParams params;
+ params.enableDebugLayer = true;
+ rhi = QRhi::create(QRhi::D3D11, &params);
+ \endcode
+
+ \note QRhiSwapChain should only be used in combination with QWindow
+ instances that have their surface type set to QSurface::OpenGLSurface.
+ There are currently no Direct3D specifics in the Windows platform support
+ of Qt and therefore there is no separate QSurface type available.
+
+ \section2 Working with existing Direct3D 11 devices
+
+ When interoperating with another graphics engine, it may be necessary to
+ get a QRhi instance that uses the same Direct3D device. This can be
+ achieved by passing a pointer to a QRhiD3D11NativeHandles to
+ QRhi::create(). Both the device and the device context must be set to a
+ non-null value then.
+
+ The QRhi does not take ownership of any of the external objects.
+
+ \note QRhi works with immediate contexts only. Deferred contexts are not
+ used in any way.
+
+ \note Regardless of using an imported or a QRhi-created device context, the
+ \c ID3D11DeviceContext1 interface (Direct3D 11.1) must be supported.
+ Initialization will fail otherwise.
+ */
+
+/*!
+ \class QRhiD3D11NativeHandles
+ \inmodule QtRhi
+ \brief Holds the D3D device and device context used by the QRhi.
+
+ \note The class uses \c{void *} as the type since including the COM-based
+ \c{d3d11.h} headers is not acceptable here. The actual types are
+ \c{ID3D11Device *} and \c{ID3D11DeviceContext *}.
+ */
+
+/*!
+ \class QRhiD3D11TextureNativeHandles
+ \inmodule QtRhi
+ \brief Holds the D3D texture object that is backing a QRhiTexture instance.
+
+ \note The class uses \c{void *} as the type since including the COM-based
+ \c{d3d11.h} headers is not acceptable here. The actual type is
+ \c{ID3D11Texture2D *}.
+ */
+
+QRhiD3D11::QRhiD3D11(QRhiD3D11InitParams *params, QRhiD3D11NativeHandles *importDevice)
+ : ofr(this)
+{
+ debugLayer = params->enableDebugLayer;
+ importedDevice = importDevice != nullptr;
+ if (importedDevice) {
+ dev = reinterpret_cast<ID3D11Device *>(importDevice->dev);
+ if (dev) {
+ ID3D11DeviceContext *ctx = reinterpret_cast<ID3D11DeviceContext *>(importDevice->context);
+ if (SUCCEEDED(ctx->QueryInterface(IID_ID3D11DeviceContext1, reinterpret_cast<void **>(&context)))) {
+ // get rid of the ref added by QueryInterface
+ ctx->Release();
+ } else {
+ qWarning("ID3D11DeviceContext1 not supported by context, cannot import");
+ importedDevice = false;
+ }
+ } else {
+ qWarning("No ID3D11Device given, cannot import");
+ importedDevice = false;
+ }
+ }
+}
+
+static QString comErrorMessage(HRESULT hr)
+{
+#ifndef Q_OS_WINRT
+ const _com_error comError(hr);
+#else
+ const _com_error comError(hr, nullptr);
+#endif
+ QString result = QLatin1String("Error 0x") + QString::number(ulong(hr), 16);
+ if (const wchar_t *msg = comError.ErrorMessage())
+ result += QLatin1String(": ") + QString::fromWCharArray(msg);
+ return result;
+}
+
+static inline uint aligned(uint v, uint byteAlign)
+{
+ return (v + byteAlign - 1) & ~(byteAlign - 1);
+}
+
+bool QRhiD3D11::create(QRhi::Flags flags)
+{
+ Q_UNUSED(flags);
+
+ uint devFlags = 0;
+ if (debugLayer)
+ devFlags |= D3D11_CREATE_DEVICE_DEBUG;
+
+ HRESULT hr;
+#if !defined(Q_CC_MINGW)
+ hasDxgi2 = QOperatingSystemVersion::current() > QOperatingSystemVersion::Windows7;
+ if (hasDxgi2)
+ hr = CreateDXGIFactory2(0, IID_IDXGIFactory2, reinterpret_cast<void **>(&dxgiFactory));
+ else
+#endif
+ hr = CreateDXGIFactory1(IID_IDXGIFactory1, reinterpret_cast<void **>(&dxgiFactory));
+
+ if (FAILED(hr)) {
+ qWarning("Failed to create DXGI factory: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+
+ if (!importedDevice) {
+ IDXGIAdapter1 *adapterToUse = nullptr;
+ IDXGIAdapter1 *adapter;
+ int requestedAdapterIndex = -1;
+ if (qEnvironmentVariableIsSet("QT_D3D_ADAPTER_INDEX"))
+ requestedAdapterIndex = qEnvironmentVariableIntValue("QT_D3D_ADAPTER_INDEX");
+ for (int adapterIndex = 0; dxgiFactory->EnumAdapters1(adapterIndex, &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) {
+ DXGI_ADAPTER_DESC1 desc;
+ adapter->GetDesc1(&desc);
+ const QString name = QString::fromUtf16((char16_t *) desc.Description);
+ qDebug("Adapter %d: '%s' (flags 0x%x)", adapterIndex, qPrintable(name), desc.Flags);
+ if (!adapterToUse && (requestedAdapterIndex < 0 || requestedAdapterIndex == adapterIndex)) {
+ adapterToUse = adapter;
+ qDebug(" using this adapter");
+ } else {
+ adapter->Release();
+ }
+ }
+ if (!adapterToUse) {
+ qWarning("No adapter");
+ return false;
+ }
+
+ ID3D11DeviceContext *ctx = nullptr;
+ HRESULT hr = D3D11CreateDevice(adapterToUse, D3D_DRIVER_TYPE_UNKNOWN, nullptr, devFlags,
+ nullptr, 0, D3D11_SDK_VERSION,
+ &dev, &featureLevel, &ctx);
+ adapterToUse->Release();
+ if (FAILED(hr)) {
+ qWarning("Failed to create D3D11 device and context: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ if (SUCCEEDED(ctx->QueryInterface(IID_ID3D11DeviceContext1, reinterpret_cast<void **>(&context)))) {
+ ctx->Release();
+ } else {
+ qWarning("ID3D11DeviceContext1 not supported");
+ return false;
+ }
+ } else {
+ Q_ASSERT(dev && context);
+ featureLevel = dev->GetFeatureLevel();
+ }
+
+ if (FAILED(context->QueryInterface(IID_ID3DUserDefinedAnnotation, reinterpret_cast<void **>(&annotations))))
+ annotations = nullptr;
+
+ nativeHandlesStruct.dev = dev;
+ nativeHandlesStruct.context = context;
+
+ return true;
+}
+
+void QRhiD3D11::destroy()
+{
+ finishActiveReadbacks();
+
+ if (annotations) {
+ annotations->Release();
+ annotations = nullptr;
+ }
+
+ if (!importedDevice) {
+ if (context) {
+ context->Release();
+ context = nullptr;
+ }
+ if (dev) {
+ dev->Release();
+ dev = nullptr;
+ }
+ }
+
+ if (dxgiFactory) {
+ dxgiFactory->Release();
+ dxgiFactory = nullptr;
+ }
+}
+
+void QRhiD3D11::reportLiveObjects(ID3D11Device *device)
+{
+ // this works only when params.enableDebugLayer was true
+ ID3D11Debug *debug;
+ if (SUCCEEDED(device->QueryInterface(IID_ID3D11Debug, reinterpret_cast<void **>(&debug)))) {
+ debug->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL);
+ debug->Release();
+ }
+}
+
+QVector<int> QRhiD3D11::supportedSampleCounts() const
+{
+ return { 1, 2, 4, 8 };
+}
+
+DXGI_SAMPLE_DESC QRhiD3D11::effectiveSampleCount(int sampleCount) const
+{
+ DXGI_SAMPLE_DESC desc;
+ desc.Count = 1;
+ desc.Quality = 0;
+
+ // Stay compatible with QSurfaceFormat and friends where samples == 0 means the same as 1.
+ int s = qBound(1, sampleCount, 64);
+
+ if (!supportedSampleCounts().contains(s)) {
+ qWarning("Attempted to set unsupported sample count %d", sampleCount);
+ return desc;
+ }
+
+ desc.Count = s;
+ if (s > 1)
+ desc.Quality = D3D11_STANDARD_MULTISAMPLE_PATTERN;
+ else
+ desc.Quality = 0;
+
+ return desc;
+}
+
+QRhiSwapChain *QRhiD3D11::createSwapChain()
+{
+ return new QD3D11SwapChain(this);
+}
+
+QRhiBuffer *QRhiD3D11::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size)
+{
+ return new QD3D11Buffer(this, type, usage, size);
+}
+
+int QRhiD3D11::ubufAlignment() const
+{
+ return 256;
+}
+
+bool QRhiD3D11::isYUpInFramebuffer() const
+{
+ return false;
+}
+
+bool QRhiD3D11::isYUpInNDC() const
+{
+ return true;
+}
+
+bool QRhiD3D11::isClipDepthZeroToOne() const
+{
+ return true;
+}
+
+QMatrix4x4 QRhiD3D11::clipSpaceCorrMatrix() const
+{
+ // Like with Vulkan, but Y is already good.
+
+ static QMatrix4x4 m;
+ if (m.isIdentity()) {
+ // NB the ctor takes row-major
+ m = QMatrix4x4(1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 0.5f, 0.5f,
+ 0.0f, 0.0f, 0.0f, 1.0f);
+ }
+ return m;
+}
+
+bool QRhiD3D11::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const
+{
+ Q_UNUSED(flags);
+
+ if (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ASTC_12x12)
+ return false;
+
+ return true;
+}
+
+bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const
+{
+ switch (feature) {
+ case QRhi::MultisampleTexture:
+ return true;
+ case QRhi::MultisampleRenderBuffer:
+ return true;
+ case QRhi::DebugMarkers:
+ return annotations != nullptr;
+ case QRhi::Timestamps:
+ return true;
+ case QRhi::Instancing:
+ return true;
+ case QRhi::CustomInstanceStepRate:
+ return true;
+ case QRhi::PrimitiveRestart:
+ return true;
+ case QRhi::NonDynamicUniformBuffers:
+ return false; // because UpdateSubresource cannot deal with this
+ case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
+ return true;
+ case QRhi::NPOTTextureRepeat:
+ return true;
+ case QRhi::RedOrAlpha8IsRed:
+ return true;
+ case QRhi::ElementIndexUint:
+ return true;
+ case QRhi::Compute:
+ return true;
+ default:
+ Q_UNREACHABLE();
+ return false;
+ }
+}
+
+int QRhiD3D11::resourceLimit(QRhi::ResourceLimit limit) const
+{
+ switch (limit) {
+ case QRhi::TextureSizeMin:
+ return 1;
+ case QRhi::TextureSizeMax:
+ return D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
+ case QRhi::MaxColorAttachments:
+ return 8;
+ case QRhi::FramesInFlight:
+ return 2; // dummy
+ default:
+ Q_UNREACHABLE();
+ return 0;
+ }
+}
+
+const QRhiNativeHandles *QRhiD3D11::nativeHandles()
+{
+ return &nativeHandlesStruct;
+}
+
+void QRhiD3D11::sendVMemStatsToProfiler()
+{
+ // nothing to do here
+}
+
+void QRhiD3D11::makeThreadLocalNativeContextCurrent()
+{
+ // nothing to do here
+}
+
+QRhiRenderBuffer *QRhiD3D11::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags)
+{
+ return new QD3D11RenderBuffer(this, type, pixelSize, sampleCount, flags);
+}
+
+QRhiTexture *QRhiD3D11::createTexture(QRhiTexture::Format format, const QSize &pixelSize,
+ int sampleCount, QRhiTexture::Flags flags)
+{
+ return new QD3D11Texture(this, format, pixelSize, sampleCount, flags);
+}
+
+QRhiSampler *QRhiD3D11::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler::AddressMode u, QRhiSampler::AddressMode v)
+{
+ return new QD3D11Sampler(this, magFilter, minFilter, mipmapMode, u, v);
+}
+
+QRhiTextureRenderTarget *QRhiD3D11::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
+ QRhiTextureRenderTarget::Flags flags)
+{
+ return new QD3D11TextureRenderTarget(this, desc, flags);
+}
+
+QRhiGraphicsPipeline *QRhiD3D11::createGraphicsPipeline()
+{
+ return new QD3D11GraphicsPipeline(this);
+}
+
+QRhiComputePipeline *QRhiD3D11::createComputePipeline()
+{
+ return new QD3D11ComputePipeline(this);
+}
+
+QRhiShaderResourceBindings *QRhiD3D11::createShaderResourceBindings()
+{
+ return new QD3D11ShaderResourceBindings(this);
+}
+
+void QRhiD3D11::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps)
+{
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass);
+ QD3D11GraphicsPipeline *psD = QRHI_RES(QD3D11GraphicsPipeline, ps);
+ const bool pipelineChanged = cbD->currentGraphicsPipeline != ps || cbD->currentPipelineGeneration != psD->generation;
+
+ if (pipelineChanged) {
+ cbD->currentGraphicsPipeline = ps;
+ cbD->currentComputePipeline = nullptr;
+ cbD->currentPipelineGeneration = psD->generation;
+
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::BindGraphicsPipeline;
+ cmd.args.bindGraphicsPipeline.ps = psD;
+ cbD->commands.append(cmd);
+ }
+}
+
+void QRhiD3D11::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
+{
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass != QD3D11CommandBuffer::NoPass);
+ QD3D11GraphicsPipeline *gfxPsD = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentGraphicsPipeline);
+ QD3D11ComputePipeline *compPsD = QRHI_RES(QD3D11ComputePipeline, cbD->currentComputePipeline);
+
+ if (!srb) {
+ if (gfxPsD)
+ srb = gfxPsD->m_shaderResourceBindings;
+ else
+ srb = compPsD->m_shaderResourceBindings;
+ }
+
+ QD3D11ShaderResourceBindings *srbD = QRHI_RES(QD3D11ShaderResourceBindings, srb);
+
+ bool hasDynamicOffsetInSrb = false;
+ bool srbUpdate = false;
+ for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
+ const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&srbD->sortedBindings[i]);
+ QD3D11ShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[i]);
+ switch (b->type) {
+ case QRhiShaderResourceBinding::UniformBuffer:
+ {
+ QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, b->u.ubuf.buf);
+ if (bufD->m_type == QRhiBuffer::Dynamic)
+ executeBufferHostWritesForCurrentFrame(bufD);
+
+ if (bufD->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) {
+ srbUpdate = true;
+ bd.ubuf.id = bufD->m_id;
+ bd.ubuf.generation = bufD->generation;
+ }
+
+ if (b->u.ubuf.hasDynamicOffset)
+ hasDynamicOffsetInSrb = true;
+ }
+ break;
+ case QRhiShaderResourceBinding::SampledTexture:
+ {
+ QD3D11Texture *texD = QRHI_RES(QD3D11Texture, b->u.stex.tex);
+ QD3D11Sampler *samplerD = QRHI_RES(QD3D11Sampler, b->u.stex.sampler);
+ if (texD->generation != bd.stex.texGeneration
+ || texD->m_id != bd.stex.texId
+ || samplerD->generation != bd.stex.samplerGeneration
+ || samplerD->m_id != bd.stex.samplerId)
+ {
+ srbUpdate = true;
+ bd.stex.texId = texD->m_id;
+ bd.stex.texGeneration = texD->generation;
+ bd.stex.samplerId = samplerD->m_id;
+ bd.stex.samplerGeneration = samplerD->generation;
+ }
+ }
+ break;
+ case QRhiShaderResourceBinding::ImageLoad:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::ImageStore:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::ImageLoadStore:
+ {
+ QD3D11Texture *texD = QRHI_RES(QD3D11Texture, b->u.simage.tex);
+ if (texD->generation != bd.simage.generation || texD->m_id != bd.simage.id) {
+ srbUpdate = true;
+ bd.simage.id = texD->m_id;
+ bd.simage.generation = texD->generation;
+ }
+ }
+ break;
+ case QRhiShaderResourceBinding::BufferLoad:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::BufferStore:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::BufferLoadStore:
+ {
+ QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, b->u.sbuf.buf);
+ if (bufD->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) {
+ srbUpdate = true;
+ bd.sbuf.id = bufD->m_id;
+ bd.sbuf.generation = bufD->generation;
+ }
+ }
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+
+ if (srbUpdate)
+ updateShaderResourceBindings(srbD);
+
+ const bool srbChanged = gfxPsD ? (cbD->currentGraphicsSrb != srb) : (cbD->currentComputeSrb != srb);
+ const bool srbRebuilt = cbD->currentSrbGeneration != srbD->generation;
+
+ if (srbChanged || srbRebuilt || srbUpdate || hasDynamicOffsetInSrb) {
+ if (gfxPsD) {
+ cbD->currentGraphicsSrb = srb;
+ cbD->currentComputeSrb = nullptr;
+ } else {
+ cbD->currentGraphicsSrb = nullptr;
+ cbD->currentComputeSrb = srb;
+ }
+ cbD->currentSrbGeneration = srbD->generation;
+
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::BindShaderResources;
+ cmd.args.bindShaderResources.srb = srbD;
+ // dynamic offsets have to be applied at the time of executing the bind
+ // operations, not here
+ cmd.args.bindShaderResources.offsetOnlyChange = !srbChanged && !srbRebuilt && !srbUpdate && hasDynamicOffsetInSrb;
+ cmd.args.bindShaderResources.dynamicOffsetCount = 0;
+ if (hasDynamicOffsetInSrb) {
+ if (dynamicOffsetCount < QD3D11CommandBuffer::Command::MAX_UBUF_BINDINGS) {
+ cmd.args.bindShaderResources.dynamicOffsetCount = dynamicOffsetCount;
+ uint *p = cmd.args.bindShaderResources.dynamicOffsetPairs;
+ for (int i = 0; i < dynamicOffsetCount; ++i) {
+ const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
+ const uint binding = dynOfs.first;
+ Q_ASSERT(aligned(dynOfs.second, 256) == dynOfs.second);
+ const uint offsetInConstants = dynOfs.second / 16;
+ *p++ = binding;
+ *p++ = offsetInConstants;
+ }
+ } else {
+ qWarning("Too many dynamic offsets (%d, max is %d)",
+ dynamicOffsetCount, QD3D11CommandBuffer::Command::MAX_UBUF_BINDINGS);
+ }
+ }
+
+ cbD->commands.append(cmd);
+ }
+}
+
+void QRhiD3D11::setVertexInput(QRhiCommandBuffer *cb,
+ int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
+ QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
+{
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass);
+
+ bool needsBindVBuf = false;
+ for (int i = 0; i < bindingCount; ++i) {
+ const int inputSlot = startBinding + i;
+ QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, bindings[i].first);
+ Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::VertexBuffer));
+ if (bufD->m_type == QRhiBuffer::Dynamic)
+ executeBufferHostWritesForCurrentFrame(bufD);
+
+ if (cbD->currentVertexBuffers[inputSlot] != bufD->buffer
+ || cbD->currentVertexOffsets[inputSlot] != bindings[i].second)
+ {
+ needsBindVBuf = true;
+ cbD->currentVertexBuffers[inputSlot] = bufD->buffer;
+ cbD->currentVertexOffsets[inputSlot] = bindings[i].second;
+ }
+ }
+
+ if (needsBindVBuf) {
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::BindVertexBuffers;
+ cmd.args.bindVertexBuffers.startSlot = startBinding;
+ cmd.args.bindVertexBuffers.slotCount = bindingCount;
+ const QVector<QRhiVertexInputBinding> inputBindings =
+ QRHI_RES(QD3D11GraphicsPipeline, cbD->currentGraphicsPipeline)->m_vertexInputLayout.bindings();
+ for (int i = 0, ie = qMin(bindingCount, inputBindings.count()); i != ie; ++i) {
+ QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, bindings[i].first);
+ cmd.args.bindVertexBuffers.buffers[i] = bufD->buffer;
+ cmd.args.bindVertexBuffers.offsets[i] = bindings[i].second;
+ cmd.args.bindVertexBuffers.strides[i] = inputBindings[i].stride();
+ }
+ cbD->commands.append(cmd);
+ }
+
+ if (indexBuf) {
+ QD3D11Buffer *ibufD = QRHI_RES(QD3D11Buffer, indexBuf);
+ Q_ASSERT(ibufD->m_usage.testFlag(QRhiBuffer::IndexBuffer));
+ if (ibufD->m_type == QRhiBuffer::Dynamic)
+ executeBufferHostWritesForCurrentFrame(ibufD);
+
+ const DXGI_FORMAT dxgiFormat = indexFormat == QRhiCommandBuffer::IndexUInt16 ? DXGI_FORMAT_R16_UINT
+ : DXGI_FORMAT_R32_UINT;
+ if (cbD->currentIndexBuffer != ibufD->buffer
+ || cbD->currentIndexOffset != indexOffset
+ || cbD->currentIndexFormat != dxgiFormat)
+ {
+ cbD->currentIndexBuffer = ibufD->buffer;
+ cbD->currentIndexOffset = indexOffset;
+ cbD->currentIndexFormat = dxgiFormat;
+
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::BindIndexBuffer;
+ cmd.args.bindIndexBuffer.buffer = ibufD->buffer;
+ cmd.args.bindIndexBuffer.offset = indexOffset;
+ cmd.args.bindIndexBuffer.format = dxgiFormat;
+ cbD->commands.append(cmd);
+ }
+ }
+}
+
+void QRhiD3D11::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport)
+{
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass);
+ Q_ASSERT(cbD->currentTarget);
+ const QSize outputSize = cbD->currentTarget->pixelSize();
+
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::Viewport;
+
+ // d3d expects top-left, QRhiViewport is bottom-left
+ float x, y, w, h;
+ if (!qrhi_toTopLeftRenderTargetRect(outputSize, viewport.viewport(), &x, &y, &w, &h))
+ return;
+
+ cmd.args.viewport.x = x;
+ cmd.args.viewport.y = y;
+ cmd.args.viewport.w = w;
+ cmd.args.viewport.h = h;
+ cmd.args.viewport.d0 = viewport.minDepth();
+ cmd.args.viewport.d1 = viewport.maxDepth();
+ cbD->commands.append(cmd);
+}
+
+void QRhiD3D11::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor)
+{
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass);
+ Q_ASSERT(cbD->currentTarget);
+ const QSize outputSize = cbD->currentTarget->pixelSize();
+
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::Scissor;
+
+ // d3d expects top-left, QRhiScissor is bottom-left
+ int x, y, w, h;
+ if (!qrhi_toTopLeftRenderTargetRect(outputSize, scissor.scissor(), &x, &y, &w, &h))
+ return;
+
+ cmd.args.scissor.x = x;
+ cmd.args.scissor.y = y;
+ cmd.args.scissor.w = w;
+ cmd.args.scissor.h = h;
+ cbD->commands.append(cmd);
+}
+
+void QRhiD3D11::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
+{
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass);
+
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::BlendConstants;
+ cmd.args.blendConstants.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentGraphicsPipeline);
+ cmd.args.blendConstants.c[0] = c.redF();
+ cmd.args.blendConstants.c[1] = c.greenF();
+ cmd.args.blendConstants.c[2] = c.blueF();
+ cmd.args.blendConstants.c[3] = c.alphaF();
+ cbD->commands.append(cmd);
+}
+
+void QRhiD3D11::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
+{
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass);
+
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::StencilRef;
+ cmd.args.stencilRef.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentGraphicsPipeline);
+ cmd.args.stencilRef.ref = refValue;
+ cbD->commands.append(cmd);
+}
+
+void QRhiD3D11::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
+ quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
+{
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass);
+
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::Draw;
+ cmd.args.draw.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentGraphicsPipeline);
+ cmd.args.draw.vertexCount = vertexCount;
+ cmd.args.draw.instanceCount = instanceCount;
+ cmd.args.draw.firstVertex = firstVertex;
+ cmd.args.draw.firstInstance = firstInstance;
+ cbD->commands.append(cmd);
+}
+
+void QRhiD3D11::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
+ quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
+{
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass);
+
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::DrawIndexed;
+ cmd.args.drawIndexed.ps = QRHI_RES(QD3D11GraphicsPipeline, cbD->currentGraphicsPipeline);
+ cmd.args.drawIndexed.indexCount = indexCount;
+ cmd.args.drawIndexed.instanceCount = instanceCount;
+ cmd.args.drawIndexed.firstIndex = firstIndex;
+ cmd.args.drawIndexed.vertexOffset = vertexOffset;
+ cmd.args.drawIndexed.firstInstance = firstInstance;
+ cbD->commands.append(cmd);
+}
+
+void QRhiD3D11::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
+{
+ if (!debugMarkers || !annotations)
+ return;
+
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::DebugMarkBegin;
+ strncpy(cmd.args.debugMark.s, name.constData(), sizeof(cmd.args.debugMark.s));
+ cmd.args.debugMark.s[sizeof(cmd.args.debugMark.s) - 1] = '\0';
+ cbD->commands.append(cmd);
+}
+
+void QRhiD3D11::debugMarkEnd(QRhiCommandBuffer *cb)
+{
+ if (!debugMarkers || !annotations)
+ return;
+
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::DebugMarkEnd;
+ cbD->commands.append(cmd);
+}
+
+void QRhiD3D11::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
+{
+ if (!debugMarkers || !annotations)
+ return;
+
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::DebugMarkMsg;
+ strncpy(cmd.args.debugMark.s, msg.constData(), sizeof(cmd.args.debugMark.s));
+ cmd.args.debugMark.s[sizeof(cmd.args.debugMark.s) - 1] = '\0';
+ cbD->commands.append(cmd);
+}
+
+const QRhiNativeHandles *QRhiD3D11::nativeHandles(QRhiCommandBuffer *cb)
+{
+ Q_UNUSED(cb);
+ return nullptr;
+}
+
+void QRhiD3D11::beginExternal(QRhiCommandBuffer *cb)
+{
+ Q_UNUSED(cb);
+ flushCommandBuffer();
+}
+
+void QRhiD3D11::endExternal(QRhiCommandBuffer *cb)
+{
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ Q_ASSERT(cbD->commands.isEmpty());
+ cbD->resetCachedState();
+ if (cbD->currentTarget) { // could be compute, no rendertarget then
+ QD3D11CommandBuffer::Command fbCmd;
+ fbCmd.cmd = QD3D11CommandBuffer::Command::SetRenderTarget;
+ fbCmd.args.setRenderTarget.rt = cbD->currentTarget;
+ cbD->commands.append(fbCmd);
+ }
+}
+
+QRhi::FrameOpResult QRhiD3D11::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags)
+{
+ Q_UNUSED(flags);
+
+ QD3D11SwapChain *swapChainD = QRHI_RES(QD3D11SwapChain, swapChain);
+ contextState.currentSwapChain = swapChainD;
+ const int currentFrameSlot = swapChainD->currentFrameSlot;
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+
+ if (swapChainD->timestampActive[currentFrameSlot]) {
+ ID3D11Query *tsDisjoint = swapChainD->timestampDisjointQuery[currentFrameSlot];
+ const int tsIdx = QD3D11SwapChain::BUFFER_COUNT * currentFrameSlot;
+ ID3D11Query *tsStart = swapChainD->timestampQuery[tsIdx];
+ ID3D11Query *tsEnd = swapChainD->timestampQuery[tsIdx + 1];
+ quint64 timestamps[2];
+ D3D11_QUERY_DATA_TIMESTAMP_DISJOINT dj;
+ bool ok = true;
+ ok &= context->GetData(tsDisjoint, &dj, sizeof(dj), D3D11_ASYNC_GETDATA_DONOTFLUSH) == S_OK;
+ ok &= context->GetData(tsEnd, &timestamps[1], sizeof(quint64), D3D11_ASYNC_GETDATA_DONOTFLUSH) == S_OK;
+ // this above is often not ready, not even in frame_where_recorded+2,
+ // not clear why. so make the whole thing async and do not touch the
+ // queries until they are finally all available in frame this+2 or
+ // this+4 or ...
+ ok &= context->GetData(tsStart, &timestamps[0], sizeof(quint64), D3D11_ASYNC_GETDATA_DONOTFLUSH) == S_OK;
+ if (ok) {
+ if (!dj.Disjoint && dj.Frequency) {
+ const float elapsedMs = (timestamps[1] - timestamps[0]) / float(dj.Frequency) * 1000.0f;
+ // finally got a value, just report it, the profiler cares about min/max/avg anyway
+ QRHI_PROF_F(swapChainFrameGpuTime(swapChain, elapsedMs));
+ }
+ swapChainD->timestampActive[currentFrameSlot] = false;
+ } // else leave timestampActive set to true, will retry in a subsequent beginFrame
+ }
+
+ swapChainD->cb.resetState();
+
+ swapChainD->rt.d.rtv[0] = swapChainD->sampleDesc.Count > 1 ?
+ swapChainD->msaaRtv[currentFrameSlot] : swapChainD->rtv[currentFrameSlot];
+ swapChainD->rt.d.dsv = swapChainD->ds ? swapChainD->ds->dsv : nullptr;
+
+ QRHI_PROF_F(beginSwapChainFrame(swapChain));
+
+ finishActiveReadbacks();
+
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiD3D11::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags)
+{
+ QD3D11SwapChain *swapChainD = QRHI_RES(QD3D11SwapChain, swapChain);
+ Q_ASSERT(contextState.currentSwapChain = swapChainD);
+ const int currentFrameSlot = swapChainD->currentFrameSlot;
+
+ ID3D11Query *tsDisjoint = swapChainD->timestampDisjointQuery[currentFrameSlot];
+ const int tsIdx = QD3D11SwapChain::BUFFER_COUNT * currentFrameSlot;
+ ID3D11Query *tsStart = swapChainD->timestampQuery[tsIdx];
+ ID3D11Query *tsEnd = swapChainD->timestampQuery[tsIdx + 1];
+ const bool recordTimestamps = tsDisjoint && tsStart && tsEnd && !swapChainD->timestampActive[currentFrameSlot];
+
+ // send all commands to the context
+ if (recordTimestamps)
+ executeCommandBuffer(&swapChainD->cb, swapChainD);
+ else
+ executeCommandBuffer(&swapChainD->cb);
+
+ if (swapChainD->sampleDesc.Count > 1) {
+ context->ResolveSubresource(swapChainD->tex[currentFrameSlot], 0,
+ swapChainD->msaaTex[currentFrameSlot], 0,
+ swapChainD->colorFormat);
+ }
+
+ // this is here because we want to include the time spent on the resolve as well
+ if (recordTimestamps) {
+ context->End(tsEnd);
+ context->End(tsDisjoint);
+ swapChainD->timestampActive[currentFrameSlot] = true;
+ }
+
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+ // this must be done before the Present
+ QRHI_PROF_F(endSwapChainFrame(swapChain, swapChainD->frameCount + 1));
+
+ if (!flags.testFlag(QRhi::SkipPresent)) {
+ const UINT presentFlags = 0;
+ HRESULT hr = swapChainD->swapChain->Present(swapChainD->swapInterval, presentFlags);
+ if (FAILED(hr))
+ qWarning("Failed to present: %s", qPrintable(comErrorMessage(hr)));
+
+ // move on to the next buffer
+ swapChainD->currentFrameSlot = (swapChainD->currentFrameSlot + 1) % QD3D11SwapChain::BUFFER_COUNT;
+ } else {
+ context->Flush();
+ }
+
+ swapChainD->frameCount += 1;
+ contextState.currentSwapChain = nullptr;
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiD3D11::beginOffscreenFrame(QRhiCommandBuffer **cb)
+{
+ ofr.active = true;
+
+ ofr.cbWrapper.resetState();
+ *cb = &ofr.cbWrapper;
+
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiD3D11::endOffscreenFrame()
+{
+ ofr.active = false;
+
+ executeCommandBuffer(&ofr.cbWrapper);
+
+ finishActiveReadbacks();
+
+ return QRhi::FrameOpSuccess;
+}
+
+static inline DXGI_FORMAT toD3DTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags)
+{
+ const bool srgb = flags.testFlag(QRhiTexture::sRGB);
+ switch (format) {
+ case QRhiTexture::RGBA8:
+ return srgb ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM;
+ case QRhiTexture::BGRA8:
+ return srgb ? DXGI_FORMAT_B8G8R8A8_UNORM_SRGB : DXGI_FORMAT_B8G8R8A8_UNORM;
+ case QRhiTexture::R8:
+ return DXGI_FORMAT_R8_UNORM;
+ case QRhiTexture::R16:
+ return DXGI_FORMAT_R16_UNORM;
+ case QRhiTexture::RED_OR_ALPHA8:
+ return DXGI_FORMAT_R8_UNORM;
+
+ case QRhiTexture::RGBA16F:
+ return DXGI_FORMAT_R16G16B16A16_FLOAT;
+ case QRhiTexture::RGBA32F:
+ return DXGI_FORMAT_R32G32B32A32_FLOAT;
+
+ case QRhiTexture::D16:
+ return DXGI_FORMAT_R16_TYPELESS;
+ case QRhiTexture::D32F:
+ return DXGI_FORMAT_R32_TYPELESS;
+
+ case QRhiTexture::BC1:
+ return srgb ? DXGI_FORMAT_BC1_UNORM_SRGB : DXGI_FORMAT_BC1_UNORM;
+ case QRhiTexture::BC2:
+ return srgb ? DXGI_FORMAT_BC2_UNORM_SRGB : DXGI_FORMAT_BC2_UNORM;
+ case QRhiTexture::BC3:
+ return srgb ? DXGI_FORMAT_BC3_UNORM_SRGB : DXGI_FORMAT_BC3_UNORM;
+ case QRhiTexture::BC4:
+ return DXGI_FORMAT_BC4_UNORM;
+ case QRhiTexture::BC5:
+ return DXGI_FORMAT_BC5_UNORM;
+ case QRhiTexture::BC6H:
+ return DXGI_FORMAT_BC6H_UF16;
+ case QRhiTexture::BC7:
+ return srgb ? DXGI_FORMAT_BC7_UNORM_SRGB : DXGI_FORMAT_BC7_UNORM;
+
+ case QRhiTexture::ETC2_RGB8:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ETC2_RGB8A1:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ETC2_RGBA8:
+ qWarning("QRhiD3D11 does not support ETC2 textures");
+ return DXGI_FORMAT_R8G8B8A8_UNORM;
+
+ case QRhiTexture::ASTC_4x4:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ASTC_5x4:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ASTC_5x5:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ASTC_6x5:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ASTC_6x6:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ASTC_8x5:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ASTC_8x6:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ASTC_8x8:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ASTC_10x5:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ASTC_10x6:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ASTC_10x8:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ASTC_10x10:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ASTC_12x10:
+ Q_FALLTHROUGH();
+ case QRhiTexture::ASTC_12x12:
+ qWarning("QRhiD3D11 does not support ASTC textures");
+ return DXGI_FORMAT_R8G8B8A8_UNORM;
+
+ default:
+ Q_UNREACHABLE();
+ return DXGI_FORMAT_R8G8B8A8_UNORM;
+ }
+}
+
+static inline QRhiTexture::Format colorTextureFormatFromDxgiFormat(DXGI_FORMAT format, QRhiTexture::Flags *flags)
+{
+ switch (format) {
+ case DXGI_FORMAT_R8G8B8A8_UNORM:
+ return QRhiTexture::RGBA8;
+ case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
+ if (flags)
+ (*flags) |= QRhiTexture::sRGB;
+ return QRhiTexture::RGBA8;
+ case DXGI_FORMAT_B8G8R8A8_UNORM:
+ return QRhiTexture::BGRA8;
+ case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
+ if (flags)
+ (*flags) |= QRhiTexture::sRGB;
+ return QRhiTexture::BGRA8;
+ case DXGI_FORMAT_R8_UNORM:
+ return QRhiTexture::R8;
+ case DXGI_FORMAT_R16_UNORM:
+ return QRhiTexture::R16;
+ default: // this cannot assert, must warn and return unknown
+ qWarning("DXGI_FORMAT %d is not a recognized uncompressed color format", format);
+ break;
+ }
+ return QRhiTexture::UnknownFormat;
+}
+
+static inline bool isDepthTextureFormat(QRhiTexture::Format format)
+{
+ switch (format) {
+ case QRhiTexture::Format::D16:
+ Q_FALLTHROUGH();
+ case QRhiTexture::Format::D32F:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+QRhi::FrameOpResult QRhiD3D11::finish()
+{
+ if (inFrame)
+ flushCommandBuffer();
+
+ finishActiveReadbacks();
+
+ return QRhi::FrameOpSuccess;
+}
+
+void QRhiD3D11::flushCommandBuffer()
+{
+ if (ofr.active) {
+ Q_ASSERT(!contextState.currentSwapChain);
+ executeCommandBuffer(&ofr.cbWrapper);
+ ofr.cbWrapper.resetCommands();
+ } else {
+ Q_ASSERT(contextState.currentSwapChain);
+ executeCommandBuffer(&contextState.currentSwapChain->cb); // no timestampSwapChain, in order to avoid timestamp mess
+ contextState.currentSwapChain->cb.resetCommands();
+ }
+}
+
+void QRhiD3D11::enqueueSubresUpload(QD3D11Texture *texD, QD3D11CommandBuffer *cbD,
+ int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc)
+{
+ UINT subres = D3D11CalcSubresource(level, layer, texD->mipLevelCount);
+ const QPoint dp = subresDesc.destinationTopLeft();
+ D3D11_BOX box;
+ box.front = 0;
+ // back, right, bottom are exclusive
+ box.back = 1;
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::UpdateSubRes;
+ cmd.args.updateSubRes.dst = texD->tex;
+ cmd.args.updateSubRes.dstSubRes = subres;
+
+ bool cmdValid = true;
+ if (!subresDesc.image().isNull()) {
+ QImage img = subresDesc.image();
+ QSize size = img.size();
+ int bpl = img.bytesPerLine();
+ if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
+ const QPoint sp = subresDesc.sourceTopLeft();
+ if (!subresDesc.sourceSize().isEmpty())
+ size = subresDesc.sourceSize();
+ if (img.depth() == 32) {
+ const int offset = sp.y() * img.bytesPerLine() + sp.x() * 4;
+ cmd.args.updateSubRes.src = cbD->retainImage(img) + offset;
+ } else {
+ img = img.copy(sp.x(), sp.y(), size.width(), size.height());
+ bpl = img.bytesPerLine();
+ cmd.args.updateSubRes.src = cbD->retainImage(img);
+ }
+ } else {
+ cmd.args.updateSubRes.src = cbD->retainImage(img);
+ }
+ box.left = dp.x();
+ box.top = dp.y();
+ box.right = dp.x() + size.width();
+ box.bottom = dp.y() + size.height();
+ cmd.args.updateSubRes.hasDstBox = true;
+ cmd.args.updateSubRes.dstBox = box;
+ cmd.args.updateSubRes.srcRowPitch = bpl;
+ } else if (!subresDesc.data().isEmpty() && isCompressedFormat(texD->m_format)) {
+ const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize)
+ : subresDesc.sourceSize();
+ quint32 bpl = 0;
+ QSize blockDim;
+ compressedFormatInfo(texD->m_format, size, &bpl, nullptr, &blockDim);
+ // Everything must be a multiple of the block width and
+ // height, so e.g. a mip level of size 2x2 will be 4x4 when it
+ // comes to the actual data.
+ box.left = aligned(dp.x(), blockDim.width());
+ box.top = aligned(dp.y(), blockDim.height());
+ box.right = aligned(dp.x() + size.width(), blockDim.width());
+ box.bottom = aligned(dp.y() + size.height(), blockDim.height());
+ cmd.args.updateSubRes.hasDstBox = true;
+ cmd.args.updateSubRes.dstBox = box;
+ cmd.args.updateSubRes.src = cbD->retainData(subresDesc.data());
+ cmd.args.updateSubRes.srcRowPitch = bpl;
+ } else if (!subresDesc.data().isEmpty()) {
+ const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize)
+ : subresDesc.sourceSize();
+ quint32 bpl = 0;
+ QSize blockDim;
+ textureFormatInfo(texD->m_format, size, &bpl, nullptr);
+ box.left = dp.x();
+ box.top = dp.y();
+ box.right = dp.x() + size.width();
+ box.bottom = dp.y() + size.height();
+ cmd.args.updateSubRes.hasDstBox = true;
+ cmd.args.updateSubRes.dstBox = box;
+ cmd.args.updateSubRes.src = cbD->retainData(subresDesc.data());
+ cmd.args.updateSubRes.srcRowPitch = bpl;
+ } else {
+ qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
+ cmdValid = false;
+ }
+ if (cmdValid)
+ cbD->commands.append(cmd);
+}
+
+void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+
+ for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : ud->dynamicBufferUpdates) {
+ QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, u.buf);
+ Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
+ memcpy(bufD->dynBuf.data() + u.offset, u.data.constData(), u.data.size());
+ bufD->hasPendingDynamicUpdates = true;
+ }
+
+ for (const QRhiResourceUpdateBatchPrivate::StaticBufferUpload &u : ud->staticBufferUploads) {
+ QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, u.buf);
+ Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
+ Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::UpdateSubRes;
+ cmd.args.updateSubRes.dst = bufD->buffer;
+ cmd.args.updateSubRes.dstSubRes = 0;
+ cmd.args.updateSubRes.src = cbD->retainData(u.data);
+ cmd.args.updateSubRes.srcRowPitch = 0;
+ // Specify the region (even when offset is 0 and all data is provided)
+ // since the ID3D11Buffer's size is rounded up to be a multiple of 256
+ // while the data we have has the original size.
+ D3D11_BOX box;
+ box.left = u.offset;
+ box.top = box.front = 0;
+ box.back = box.bottom = 1;
+ box.right = u.offset + u.data.size(); // no -1: right, bottom, back are exclusive, see D3D11_BOX doc
+ cmd.args.updateSubRes.hasDstBox = true;
+ cmd.args.updateSubRes.dstBox = box;
+ cbD->commands.append(cmd);
+ }
+
+ for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) {
+ if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
+ QD3D11Texture *texD = QRHI_RES(QD3D11Texture, u.upload.tex);
+ for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
+ for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
+ for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.upload.subresDesc[layer][level]))
+ enqueueSubresUpload(texD, cbD, layer, level, subresDesc);
+ }
+ }
+ } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) {
+ Q_ASSERT(u.copy.src && u.copy.dst);
+ QD3D11Texture *srcD = QRHI_RES(QD3D11Texture, u.copy.src);
+ QD3D11Texture *dstD = QRHI_RES(QD3D11Texture, u.copy.dst);
+ UINT srcSubRes = D3D11CalcSubresource(u.copy.desc.sourceLevel(), u.copy.desc.sourceLayer(), srcD->mipLevelCount);
+ UINT dstSubRes = D3D11CalcSubresource(u.copy.desc.destinationLevel(), u.copy.desc.destinationLayer(), dstD->mipLevelCount);
+ const QPoint dp = u.copy.desc.destinationTopLeft();
+ const QSize size = u.copy.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : u.copy.desc.pixelSize();
+ const QPoint sp = u.copy.desc.sourceTopLeft();
+ D3D11_BOX srcBox;
+ srcBox.left = sp.x();
+ srcBox.top = sp.y();
+ srcBox.front = 0;
+ // back, right, bottom are exclusive
+ srcBox.right = srcBox.left + size.width();
+ srcBox.bottom = srcBox.top + size.height();
+ srcBox.back = 1;
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::CopySubRes;
+ cmd.args.copySubRes.dst = dstD->tex;
+ cmd.args.copySubRes.dstSubRes = dstSubRes;
+ cmd.args.copySubRes.dstX = dp.x();
+ cmd.args.copySubRes.dstY = dp.y();
+ cmd.args.copySubRes.src = srcD->tex;
+ cmd.args.copySubRes.srcSubRes = srcSubRes;
+ cmd.args.copySubRes.hasSrcBox = true;
+ cmd.args.copySubRes.srcBox = srcBox;
+ cbD->commands.append(cmd);
+ } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
+ ActiveReadback aRb;
+ aRb.desc = u.read.rb;
+ aRb.result = u.read.result;
+
+ ID3D11Resource *src;
+ DXGI_FORMAT dxgiFormat;
+ QSize pixelSize;
+ QRhiTexture::Format format;
+ UINT subres = 0;
+ QD3D11Texture *texD = QRHI_RES(QD3D11Texture, u.read.rb.texture());
+ QD3D11SwapChain *swapChainD = nullptr;
+
+ if (texD) {
+ if (texD->sampleDesc.Count > 1) {
+ qWarning("Multisample texture cannot be read back");
+ continue;
+ }
+ src = texD->tex;
+ dxgiFormat = texD->dxgiFormat;
+ pixelSize = u.read.rb.level() > 0 ? q->sizeForMipLevel(u.read.rb.level(), texD->m_pixelSize) : texD->m_pixelSize;
+ format = texD->m_format;
+ subres = D3D11CalcSubresource(u.read.rb.level(), u.read.rb.layer(), texD->mipLevelCount);
+ } else {
+ Q_ASSERT(contextState.currentSwapChain);
+ swapChainD = QRHI_RES(QD3D11SwapChain, contextState.currentSwapChain);
+ if (swapChainD->sampleDesc.Count > 1) {
+ // Unlike with textures, reading back a multisample swapchain image
+ // has to be supported. Insert a resolve.
+ QD3D11CommandBuffer::Command rcmd;
+ rcmd.cmd = QD3D11CommandBuffer::Command::ResolveSubRes;
+ rcmd.args.resolveSubRes.dst = swapChainD->tex[swapChainD->currentFrameSlot];
+ rcmd.args.resolveSubRes.dstSubRes = 0;
+ rcmd.args.resolveSubRes.src = swapChainD->msaaTex[swapChainD->currentFrameSlot];
+ rcmd.args.resolveSubRes.srcSubRes = 0;
+ rcmd.args.resolveSubRes.format = swapChainD->colorFormat;
+ cbD->commands.append(rcmd);
+ }
+ src = swapChainD->tex[swapChainD->currentFrameSlot];
+ dxgiFormat = swapChainD->colorFormat;
+ pixelSize = swapChainD->pixelSize;
+ format = colorTextureFormatFromDxgiFormat(dxgiFormat, nullptr);
+ if (format == QRhiTexture::UnknownFormat)
+ continue;
+ }
+ quint32 bufSize = 0;
+ quint32 bpl = 0;
+ textureFormatInfo(format, pixelSize, &bpl, &bufSize);
+
+ D3D11_TEXTURE2D_DESC desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.Width = pixelSize.width();
+ desc.Height = pixelSize.height();
+ desc.MipLevels = 1;
+ desc.ArraySize = 1;
+ desc.Format = dxgiFormat;
+ desc.SampleDesc.Count = 1;
+ desc.Usage = D3D11_USAGE_STAGING;
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ ID3D11Texture2D *stagingTex;
+ HRESULT hr = dev->CreateTexture2D(&desc, nullptr, &stagingTex);
+ if (FAILED(hr)) {
+ qWarning("Failed to create readback staging texture: %s", qPrintable(comErrorMessage(hr)));
+ return;
+ }
+ QRHI_PROF_F(newReadbackBuffer(quint64(quintptr(stagingTex)),
+ texD ? static_cast<QRhiResource *>(texD) : static_cast<QRhiResource *>(swapChainD),
+ bufSize));
+
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::CopySubRes;
+ cmd.args.copySubRes.dst = stagingTex;
+ cmd.args.copySubRes.dstSubRes = 0;
+ cmd.args.copySubRes.dstX = 0;
+ cmd.args.copySubRes.dstY = 0;
+ cmd.args.copySubRes.src = src;
+ cmd.args.copySubRes.srcSubRes = subres;
+ cmd.args.copySubRes.hasSrcBox = false;
+ cbD->commands.append(cmd);
+
+ aRb.stagingTex = stagingTex;
+ aRb.bufSize = bufSize;
+ aRb.bpl = bpl;
+ aRb.pixelSize = pixelSize;
+ aRb.format = format;
+
+ activeReadbacks.append(aRb);
+ } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::MipGen) {
+ Q_ASSERT(u.mipgen.tex->flags().testFlag(QRhiTexture::UsedWithGenerateMips));
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::GenMip;
+ cmd.args.genMip.srv = QRHI_RES(QD3D11Texture, u.mipgen.tex)->srv;
+ cbD->commands.append(cmd);
+ }
+ }
+
+ ud->free();
+}
+
+void QRhiD3D11::finishActiveReadbacks()
+{
+ QVarLengthArray<std::function<void()>, 4> completedCallbacks;
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+
+ for (int i = activeReadbacks.count() - 1; i >= 0; --i) {
+ const QRhiD3D11::ActiveReadback &aRb(activeReadbacks[i]);
+ aRb.result->format = aRb.format;
+ aRb.result->pixelSize = aRb.pixelSize;
+ aRb.result->data.resize(aRb.bufSize);
+
+ D3D11_MAPPED_SUBRESOURCE mp;
+ HRESULT hr = context->Map(aRb.stagingTex, 0, D3D11_MAP_READ, 0, &mp);
+ if (FAILED(hr)) {
+ qWarning("Failed to map readback staging texture: %s", qPrintable(comErrorMessage(hr)));
+ aRb.stagingTex->Release();
+ continue;
+ }
+ // nothing says the rows are tightly packed in the texture, must take
+ // the stride into account
+ char *dst = aRb.result->data.data();
+ char *src = static_cast<char *>(mp.pData);
+ for (int y = 0, h = aRb.pixelSize.height(); y != h; ++y) {
+ memcpy(dst, src, aRb.bpl);
+ dst += aRb.bpl;
+ src += mp.RowPitch;
+ }
+ context->Unmap(aRb.stagingTex, 0);
+
+ aRb.stagingTex->Release();
+ QRHI_PROF_F(releaseReadbackBuffer(quint64(quintptr(aRb.stagingTex))));
+
+ if (aRb.result->completed)
+ completedCallbacks.append(aRb.result->completed);
+
+ activeReadbacks.removeAt(i);
+ }
+
+ for (auto f : completedCallbacks)
+ f();
+}
+
+static inline QD3D11RenderTargetData *rtData(QRhiRenderTarget *rt)
+{
+ switch (rt->resourceType()) {
+ case QRhiResource::RenderTarget:
+ return &QRHI_RES(QD3D11ReferenceRenderTarget, rt)->d;
+ case QRhiResource::TextureRenderTarget:
+ return &QRHI_RES(QD3D11TextureRenderTarget, rt)->d;
+ default:
+ Q_UNREACHABLE();
+ return nullptr;
+ }
+}
+
+void QRhiD3D11::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_ASSERT(QRHI_RES(QD3D11CommandBuffer, cb)->recordingPass == QD3D11CommandBuffer::NoPass);
+
+ enqueueResourceUpdates(cb, resourceUpdates);
+}
+
+void QRhiD3D11::beginPass(QRhiCommandBuffer *cb,
+ QRhiRenderTarget *rt,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ QRhiResourceUpdateBatch *resourceUpdates)
+{
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::NoPass);
+
+ if (resourceUpdates)
+ enqueueResourceUpdates(cb, resourceUpdates);
+
+ bool wantsColorClear = true;
+ bool wantsDsClear = true;
+ QD3D11RenderTargetData *rtD = rtData(rt);
+ if (rt->resourceType() == QRhiRenderTarget::TextureRenderTarget) {
+ QD3D11TextureRenderTarget *rtTex = QRHI_RES(QD3D11TextureRenderTarget, rt);
+ wantsColorClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents);
+ wantsDsClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
+ }
+
+ QD3D11CommandBuffer::Command fbCmd;
+ fbCmd.cmd = QD3D11CommandBuffer::Command::ResetShaderResources;
+ cbD->commands.append(fbCmd);
+ fbCmd.cmd = QD3D11CommandBuffer::Command::SetRenderTarget;
+ fbCmd.args.setRenderTarget.rt = rt;
+ cbD->commands.append(fbCmd);
+
+ QD3D11CommandBuffer::Command clearCmd;
+ clearCmd.cmd = QD3D11CommandBuffer::Command::Clear;
+ clearCmd.args.clear.rt = rt;
+ clearCmd.args.clear.mask = 0;
+ if (rtD->colorAttCount && wantsColorClear)
+ clearCmd.args.clear.mask |= QD3D11CommandBuffer::Command::Color;
+ if (rtD->dsAttCount && wantsDsClear)
+ clearCmd.args.clear.mask |= QD3D11CommandBuffer::Command::Depth | QD3D11CommandBuffer::Command::Stencil;
+
+ clearCmd.args.clear.c[0] = colorClearValue.redF();
+ clearCmd.args.clear.c[1] = colorClearValue.greenF();
+ clearCmd.args.clear.c[2] = colorClearValue.blueF();
+ clearCmd.args.clear.c[3] = colorClearValue.alphaF();
+ clearCmd.args.clear.d = depthStencilClearValue.depthClearValue();
+ clearCmd.args.clear.s = depthStencilClearValue.stencilClearValue();
+ cbD->commands.append(clearCmd);
+
+ cbD->recordingPass = QD3D11CommandBuffer::RenderPass;
+ cbD->currentTarget = rt;
+}
+
+void QRhiD3D11::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::RenderPass);
+
+ if (cbD->currentTarget->resourceType() == QRhiResource::TextureRenderTarget) {
+ QD3D11TextureRenderTarget *rtTex = QRHI_RES(QD3D11TextureRenderTarget, cbD->currentTarget);
+ const QVector<QRhiColorAttachment> colorAttachments = rtTex->m_desc.colorAttachments();
+ for (int att = 0, attCount = colorAttachments.count(); att != attCount; ++att) {
+ const QRhiColorAttachment &colorAtt(colorAttachments[att]);
+ if (!colorAtt.resolveTexture())
+ continue;
+
+ QD3D11Texture *dstTexD = QRHI_RES(QD3D11Texture, colorAtt.resolveTexture());
+ QD3D11Texture *srcTexD = QRHI_RES(QD3D11Texture, colorAtt.texture());
+ QD3D11RenderBuffer *srcRbD = QRHI_RES(QD3D11RenderBuffer, colorAtt.renderBuffer());
+ Q_ASSERT(srcTexD || srcRbD);
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::ResolveSubRes;
+ cmd.args.resolveSubRes.dst = dstTexD->tex;
+ cmd.args.resolveSubRes.dstSubRes = D3D11CalcSubresource(colorAtt.resolveLevel(),
+ colorAtt.resolveLayer(),
+ dstTexD->mipLevelCount);
+ if (srcTexD) {
+ cmd.args.resolveSubRes.src = srcTexD->tex;
+ if (srcTexD->dxgiFormat != dstTexD->dxgiFormat) {
+ qWarning("Resolve source and destination formats do not match");
+ continue;
+ }
+ if (srcTexD->sampleDesc.Count <= 1) {
+ qWarning("Cannot resolve a non-multisample texture");
+ continue;
+ }
+ if (srcTexD->m_pixelSize != dstTexD->m_pixelSize) {
+ qWarning("Resolve source and destination sizes do not match");
+ continue;
+ }
+ } else {
+ cmd.args.resolveSubRes.src = srcRbD->tex;
+ if (srcRbD->dxgiFormat != dstTexD->dxgiFormat) {
+ qWarning("Resolve source and destination formats do not match");
+ continue;
+ }
+ if (srcRbD->m_pixelSize != dstTexD->m_pixelSize) {
+ qWarning("Resolve source and destination sizes do not match");
+ continue;
+ }
+ }
+ cmd.args.resolveSubRes.srcSubRes = D3D11CalcSubresource(0, colorAtt.layer(), 1);
+ cmd.args.resolveSubRes.format = dstTexD->dxgiFormat;
+ cbD->commands.append(cmd);
+ }
+ }
+
+ cbD->recordingPass = QD3D11CommandBuffer::NoPass;
+ cbD->currentTarget = nullptr;
+
+ if (resourceUpdates)
+ enqueueResourceUpdates(cb, resourceUpdates);
+}
+
+void QRhiD3D11::beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::NoPass);
+
+ if (resourceUpdates)
+ enqueueResourceUpdates(cb, resourceUpdates);
+
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::ResetShaderResources;
+ cbD->commands.append(cmd);
+
+ cbD->recordingPass = QD3D11CommandBuffer::ComputePass;
+}
+
+void QRhiD3D11::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::ComputePass);
+
+ cbD->recordingPass = QD3D11CommandBuffer::NoPass;
+
+ if (resourceUpdates)
+ enqueueResourceUpdates(cb, resourceUpdates);
+}
+
+void QRhiD3D11::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps)
+{
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::ComputePass);
+ QD3D11ComputePipeline *psD = QRHI_RES(QD3D11ComputePipeline, ps);
+ const bool pipelineChanged = cbD->currentComputePipeline != ps || cbD->currentPipelineGeneration != psD->generation;
+
+ if (pipelineChanged) {
+ cbD->currentGraphicsPipeline = nullptr;
+ cbD->currentComputePipeline = psD;
+ cbD->currentPipelineGeneration = psD->generation;
+
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::BindComputePipeline;
+ cmd.args.bindComputePipeline.ps = psD;
+ cbD->commands.append(cmd);
+ }
+}
+
+void QRhiD3D11::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
+{
+ QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QD3D11CommandBuffer::ComputePass);
+
+ QD3D11CommandBuffer::Command cmd;
+ cmd.cmd = QD3D11CommandBuffer::Command::Dispatch;
+ cmd.args.dispatch.x = x;
+ cmd.args.dispatch.y = y;
+ cmd.args.dispatch.z = z;
+ cbD->commands.append(cmd);
+}
+
+void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD)
+{
+ srbD->vsubufs.clear();
+ srbD->vsubufoffsets.clear();
+ srbD->vsubufsizes.clear();
+
+ srbD->fsubufs.clear();
+ srbD->fsubufoffsets.clear();
+ srbD->fsubufsizes.clear();
+
+ srbD->csubufs.clear();
+ srbD->csubufoffsets.clear();
+ srbD->csubufsizes.clear();
+
+ srbD->vssamplers.clear();
+ srbD->vsshaderresources.clear();
+
+ srbD->fssamplers.clear();
+ srbD->fsshaderresources.clear();
+
+ srbD->cssamplers.clear();
+ srbD->csshaderresources.clear();
+
+ srbD->csUAVs.clear();
+
+ for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
+ const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&srbD->sortedBindings[i]);
+ QD3D11ShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[i]);
+ switch (b->type) {
+ case QRhiShaderResourceBinding::UniformBuffer:
+ {
+ QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, b->u.ubuf.buf);
+ Q_ASSERT(aligned(b->u.ubuf.offset, 256) == b->u.ubuf.offset);
+ bd.ubuf.id = bufD->m_id;
+ bd.ubuf.generation = bufD->generation;
+ // dynamic ubuf offsets are not considered here, those are baked in
+ // at a later stage, which is good as vsubufoffsets and friends are
+ // per-srb, not per-setShaderResources call
+ const uint offsetInConstants = b->u.ubuf.offset / 16;
+ // size must be 16 mult. (in constants, i.e. multiple of 256 bytes).
+ // We can round up if needed since the buffers's actual size
+ // (ByteWidth) is always a multiple of 256.
+ const uint sizeInConstants = aligned(b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size, 256) / 16;
+ if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
+ srbD->vsubufs.feed(b->binding, bufD->buffer);
+ srbD->vsubufoffsets.feed(b->binding, offsetInConstants);
+ srbD->vsubufsizes.feed(b->binding, sizeInConstants);
+ }
+ if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
+ srbD->fsubufs.feed(b->binding, bufD->buffer);
+ srbD->fsubufoffsets.feed(b->binding, offsetInConstants);
+ srbD->fsubufsizes.feed(b->binding, sizeInConstants);
+ }
+ if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
+ srbD->csubufs.feed(b->binding, bufD->buffer);
+ srbD->csubufoffsets.feed(b->binding, offsetInConstants);
+ srbD->csubufsizes.feed(b->binding, sizeInConstants);
+ }
+ }
+ break;
+ case QRhiShaderResourceBinding::SampledTexture:
+ {
+ // A sampler with binding N is mapped to a HLSL sampler and texture
+ // with registers sN and tN by SPIRV-Cross.
+ QD3D11Texture *texD = QRHI_RES(QD3D11Texture, b->u.stex.tex);
+ QD3D11Sampler *samplerD = QRHI_RES(QD3D11Sampler, b->u.stex.sampler);
+ bd.stex.texId = texD->m_id;
+ bd.stex.texGeneration = texD->generation;
+ bd.stex.samplerId = samplerD->m_id;
+ bd.stex.samplerGeneration = samplerD->generation;
+ if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
+ srbD->vssamplers.feed(b->binding, samplerD->samplerState);
+ srbD->vsshaderresources.feed(b->binding, texD->srv);
+ }
+ if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
+ srbD->fssamplers.feed(b->binding, samplerD->samplerState);
+ srbD->fsshaderresources.feed(b->binding, texD->srv);
+ }
+ if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
+ srbD->cssamplers.feed(b->binding, samplerD->samplerState);
+ srbD->csshaderresources.feed(b->binding, texD->srv);
+ }
+ }
+ break;
+ case QRhiShaderResourceBinding::ImageLoad:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::ImageStore:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::ImageLoadStore:
+ {
+ QD3D11Texture *texD = QRHI_RES(QD3D11Texture, b->u.simage.tex);
+ bd.simage.id = texD->m_id;
+ bd.simage.generation = texD->generation;
+ if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
+ ID3D11UnorderedAccessView *uav = texD->unorderedAccessViewForLevel(b->u.simage.level);
+ if (uav)
+ srbD->csUAVs.feed(b->binding, uav);
+ } else {
+ qWarning("Unordered access only supported at compute stage");
+ }
+ }
+ break;
+ case QRhiShaderResourceBinding::BufferLoad:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::BufferStore:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::BufferLoadStore:
+ {
+ QD3D11Buffer *bufD = QRHI_RES(QD3D11Buffer, b->u.sbuf.buf);
+ bd.sbuf.id = bufD->m_id;
+ bd.sbuf.generation = bufD->generation;
+ if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
+ ID3D11UnorderedAccessView *uav = bufD->unorderedAccessView();
+ if (uav)
+ srbD->csUAVs.feed(b->binding, uav);
+ } else {
+ qWarning("Unordered access only supported at compute stage");
+ }
+ }
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+
+ srbD->vsubufs.finish();
+ srbD->vsubufoffsets.finish();
+ srbD->vsubufsizes.finish();
+
+ srbD->fsubufs.finish();
+ srbD->fsubufoffsets.finish();
+ srbD->fsubufsizes.finish();
+
+ srbD->csubufs.finish();
+ srbD->csubufoffsets.finish();
+ srbD->csubufsizes.finish();
+
+ srbD->vssamplers.finish();
+ srbD->vsshaderresources.finish();
+
+ srbD->fssamplers.finish();
+ srbD->fsshaderresources.finish();
+
+ srbD->cssamplers.finish();
+ srbD->csshaderresources.finish();
+
+ srbD->csUAVs.finish();
+}
+
+void QRhiD3D11::executeBufferHostWritesForCurrentFrame(QD3D11Buffer *bufD)
+{
+ if (!bufD->hasPendingDynamicUpdates)
+ return;
+
+ Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
+ bufD->hasPendingDynamicUpdates = false;
+ D3D11_MAPPED_SUBRESOURCE mp;
+ HRESULT hr = context->Map(bufD->buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mp);
+ if (SUCCEEDED(hr)) {
+ memcpy(mp.pData, bufD->dynBuf.constData(), bufD->dynBuf.size());
+ context->Unmap(bufD->buffer, 0);
+ } else {
+ qWarning("Failed to map buffer: %s", qPrintable(comErrorMessage(hr)));
+ }
+}
+
+static void applyDynamicOffsets(QVarLengthArray<UINT, 4> *offsets,
+ int batchIndex,
+ QRhiBatchedBindings<ID3D11Buffer *> *ubufs,
+ QRhiBatchedBindings<UINT> *ubufoffsets,
+ const uint *dynOfsPairs, int dynOfsPairCount)
+{
+ const UINT count = ubufs->batches[batchIndex].resources.count();
+ const UINT startBinding = ubufs->batches[batchIndex].startBinding;
+ *offsets = ubufoffsets->batches[batchIndex].resources;
+ for (UINT b = 0; b < count; ++b) {
+ for (int di = 0; di < dynOfsPairCount; ++di) {
+ const uint binding = dynOfsPairs[2 * di];
+ if (binding == startBinding + b) {
+ const uint offsetInConstants = dynOfsPairs[2 * di + 1];
+ (*offsets)[b] = offsetInConstants;
+ break;
+ }
+ }
+ }
+}
+
+void QRhiD3D11::bindShaderResources(QD3D11ShaderResourceBindings *srbD,
+ const uint *dynOfsPairs, int dynOfsPairCount,
+ bool offsetOnlyChange)
+{
+ if (!offsetOnlyChange) {
+ for (const auto &batch : srbD->vssamplers.batches)
+ context->VSSetSamplers(batch.startBinding, batch.resources.count(), batch.resources.constData());
+
+ for (const auto &batch : srbD->vsshaderresources.batches) {
+ context->VSSetShaderResources(batch.startBinding, batch.resources.count(), batch.resources.constData());
+ contextState.vsHighestActiveSrvBinding = qMax<int>(contextState.vsHighestActiveSrvBinding,
+ batch.startBinding + batch.resources.count() - 1);
+ }
+
+ for (const auto &batch : srbD->fssamplers.batches)
+ context->PSSetSamplers(batch.startBinding, batch.resources.count(), batch.resources.constData());
+
+ for (const auto &batch : srbD->fsshaderresources.batches) {
+ context->PSSetShaderResources(batch.startBinding, batch.resources.count(), batch.resources.constData());
+ contextState.fsHighestActiveSrvBinding = qMax<int>(contextState.fsHighestActiveSrvBinding,
+ batch.startBinding + batch.resources.count() - 1);
+ }
+
+ for (const auto &batch : srbD->cssamplers.batches)
+ context->CSSetSamplers(batch.startBinding, batch.resources.count(), batch.resources.constData());
+
+ for (const auto &batch : srbD->csshaderresources.batches) {
+ context->CSSetShaderResources(batch.startBinding, batch.resources.count(), batch.resources.constData());
+ contextState.csHighestActiveSrvBinding = qMax<int>(contextState.csHighestActiveSrvBinding,
+ batch.startBinding + batch.resources.count() - 1);
+ }
+ }
+
+ for (int i = 0, ie = srbD->vsubufs.batches.count(); i != ie; ++i) {
+ if (!dynOfsPairCount) {
+ context->VSSetConstantBuffers1(srbD->vsubufs.batches[i].startBinding,
+ srbD->vsubufs.batches[i].resources.count(),
+ srbD->vsubufs.batches[i].resources.constData(),
+ srbD->vsubufoffsets.batches[i].resources.constData(),
+ srbD->vsubufsizes.batches[i].resources.constData());
+ } else {
+ QVarLengthArray<UINT, 4> offsets;
+ applyDynamicOffsets(&offsets, i, &srbD->vsubufs, &srbD->vsubufoffsets, dynOfsPairs, dynOfsPairCount);
+ context->VSSetConstantBuffers1(srbD->vsubufs.batches[i].startBinding,
+ srbD->vsubufs.batches[i].resources.count(),
+ srbD->vsubufs.batches[i].resources.constData(),
+ offsets.constData(),
+ srbD->vsubufsizes.batches[i].resources.constData());
+ }
+ }
+
+ for (int i = 0, ie = srbD->fsubufs.batches.count(); i != ie; ++i) {
+ if (!dynOfsPairCount) {
+ context->PSSetConstantBuffers1(srbD->fsubufs.batches[i].startBinding,
+ srbD->fsubufs.batches[i].resources.count(),
+ srbD->fsubufs.batches[i].resources.constData(),
+ srbD->fsubufoffsets.batches[i].resources.constData(),
+ srbD->fsubufsizes.batches[i].resources.constData());
+ } else {
+ QVarLengthArray<UINT, 4> offsets;
+ applyDynamicOffsets(&offsets, i, &srbD->fsubufs, &srbD->fsubufoffsets, dynOfsPairs, dynOfsPairCount);
+ context->PSSetConstantBuffers1(srbD->fsubufs.batches[i].startBinding,
+ srbD->fsubufs.batches[i].resources.count(),
+ srbD->fsubufs.batches[i].resources.constData(),
+ offsets.constData(),
+ srbD->fsubufsizes.batches[i].resources.constData());
+ }
+ }
+
+ for (int i = 0, ie = srbD->csubufs.batches.count(); i != ie; ++i) {
+ if (!dynOfsPairCount) {
+ context->CSSetConstantBuffers1(srbD->csubufs.batches[i].startBinding,
+ srbD->csubufs.batches[i].resources.count(),
+ srbD->csubufs.batches[i].resources.constData(),
+ srbD->csubufoffsets.batches[i].resources.constData(),
+ srbD->csubufsizes.batches[i].resources.constData());
+ } else {
+ QVarLengthArray<UINT, 4> offsets;
+ applyDynamicOffsets(&offsets, i, &srbD->csubufs, &srbD->csubufoffsets, dynOfsPairs, dynOfsPairCount);
+ context->CSSetConstantBuffers1(srbD->csubufs.batches[i].startBinding,
+ srbD->csubufs.batches[i].resources.count(),
+ srbD->csubufs.batches[i].resources.constData(),
+ offsets.constData(),
+ srbD->csubufsizes.batches[i].resources.constData());
+ }
+ }
+
+ for (int i = 0, ie = srbD->csUAVs.batches.count(); i != ie; ++i) {
+ const uint startBinding = srbD->csUAVs.batches[i].startBinding;
+ const uint count = srbD->csUAVs.batches[i].resources.count();
+ context->CSSetUnorderedAccessViews(startBinding,
+ count,
+ srbD->csUAVs.batches[i].resources.constData(),
+ nullptr);
+ contextState.csHighestActiveUavBinding = qMax<int>(contextState.csHighestActiveUavBinding,
+ startBinding + count - 1);
+ }
+}
+
+void QRhiD3D11::resetShaderResources()
+{
+ // Output cannot be bound on input etc.
+
+ if (contextState.vsHasIndexBufferBound) {
+ context->IASetIndexBuffer(nullptr, DXGI_FORMAT_R16_UINT, 0);
+ contextState.vsHasIndexBufferBound = false;
+ }
+
+ if (contextState.vsHighestActiveVertexBufferBinding >= 0) {
+ const int count = contextState.vsHighestActiveVertexBufferBinding + 1;
+ QVarLengthArray<ID3D11Buffer *, D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT> nullbufs(count);
+ for (int i = 0; i < count; ++i)
+ nullbufs[i] = nullptr;
+ QVarLengthArray<UINT, D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT> nullstrides(count);
+ for (int i = 0; i < count; ++i)
+ nullstrides[i] = 0;
+ QVarLengthArray<UINT, D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT> nulloffsets(count);
+ for (int i = 0; i < count; ++i)
+ nulloffsets[i] = 0;
+ context->IASetVertexBuffers(0, count, nullbufs.constData(), nullstrides.constData(), nulloffsets.constData());
+ contextState.vsHighestActiveVertexBufferBinding = -1;
+ }
+
+ int nullsrvCount = qMax(contextState.vsHighestActiveSrvBinding, contextState.fsHighestActiveSrvBinding);
+ nullsrvCount = qMax(nullsrvCount, contextState.csHighestActiveSrvBinding);
+ nullsrvCount += 1;
+ if (nullsrvCount > 0) {
+ QVarLengthArray<ID3D11ShaderResourceView *,
+ D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT> nullsrvs(nullsrvCount);
+ for (int i = 0; i < nullsrvs.count(); ++i)
+ nullsrvs[i] = nullptr;
+ if (contextState.vsHighestActiveSrvBinding >= 0) {
+ context->VSSetShaderResources(0, contextState.vsHighestActiveSrvBinding + 1, nullsrvs.constData());
+ contextState.vsHighestActiveSrvBinding = -1;
+ }
+ if (contextState.fsHighestActiveSrvBinding >= 0) {
+ context->PSSetShaderResources(0, contextState.fsHighestActiveSrvBinding + 1, nullsrvs.constData());
+ contextState.fsHighestActiveSrvBinding = -1;
+ }
+ if (contextState.csHighestActiveSrvBinding >= 0) {
+ context->CSSetShaderResources(0, contextState.csHighestActiveSrvBinding + 1, nullsrvs.constData());
+ contextState.csHighestActiveSrvBinding = -1;
+ }
+ }
+
+ if (contextState.csHighestActiveUavBinding >= 0) {
+ const int nulluavCount = contextState.csHighestActiveUavBinding + 1;
+ QVarLengthArray<ID3D11UnorderedAccessView *,
+ D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT> nulluavs(nulluavCount);
+ for (int i = 0; i < nulluavCount; ++i)
+ nulluavs[i] = nullptr;
+ context->CSSetUnorderedAccessViews(0, nulluavCount, nulluavs.constData(), nullptr);
+ contextState.csHighestActiveUavBinding = -1;
+ }
+}
+
+void QRhiD3D11::executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain *timestampSwapChain)
+{
+ quint32 stencilRef = 0;
+ float blendConstants[] = { 1, 1, 1, 1 };
+
+ if (timestampSwapChain) {
+ const int currentFrameSlot = timestampSwapChain->currentFrameSlot;
+ ID3D11Query *tsDisjoint = timestampSwapChain->timestampDisjointQuery[currentFrameSlot];
+ const int tsIdx = QD3D11SwapChain::BUFFER_COUNT * currentFrameSlot;
+ ID3D11Query *tsStart = timestampSwapChain->timestampQuery[tsIdx];
+ if (tsDisjoint && tsStart && !timestampSwapChain->timestampActive[currentFrameSlot]) {
+ // The timestamps seem to include vsync time with Present(1), except
+ // when running on a non-primary gpu. This is not ideal. So try working
+ // it around by issuing a semi-fake OMSetRenderTargets early and
+ // writing the first timestamp only afterwards.
+ context->Begin(tsDisjoint);
+ QD3D11RenderTargetData *rtD = rtData(&timestampSwapChain->rt);
+ context->OMSetRenderTargets(rtD->colorAttCount, rtD->colorAttCount ? rtD->rtv : nullptr, rtD->dsv);
+ context->End(tsStart); // just record a timestamp, no Begin needed
+ }
+ }
+
+ for (const QD3D11CommandBuffer::Command &cmd : qAsConst(cbD->commands)) {
+ switch (cmd.cmd) {
+ case QD3D11CommandBuffer::Command::ResetShaderResources:
+ resetShaderResources();
+ break;
+ case QD3D11CommandBuffer::Command::SetRenderTarget:
+ {
+ QD3D11RenderTargetData *rtD = rtData(cmd.args.setRenderTarget.rt);
+ context->OMSetRenderTargets(rtD->colorAttCount, rtD->colorAttCount ? rtD->rtv : nullptr, rtD->dsv);
+ }
+ break;
+ case QD3D11CommandBuffer::Command::Clear:
+ {
+ QD3D11RenderTargetData *rtD = rtData(cmd.args.clear.rt);
+ if (cmd.args.clear.mask & QD3D11CommandBuffer::Command::Color) {
+ for (int i = 0; i < rtD->colorAttCount; ++i)
+ context->ClearRenderTargetView(rtD->rtv[i], cmd.args.clear.c);
+ }
+ uint ds = 0;
+ if (cmd.args.clear.mask & QD3D11CommandBuffer::Command::Depth)
+ ds |= D3D11_CLEAR_DEPTH;
+ if (cmd.args.clear.mask & QD3D11CommandBuffer::Command::Stencil)
+ ds |= D3D11_CLEAR_STENCIL;
+ if (ds)
+ context->ClearDepthStencilView(rtD->dsv, ds, cmd.args.clear.d, cmd.args.clear.s);
+ }
+ break;
+ case QD3D11CommandBuffer::Command::Viewport:
+ {
+ D3D11_VIEWPORT v;
+ v.TopLeftX = cmd.args.viewport.x;
+ v.TopLeftY = cmd.args.viewport.y;
+ v.Width = cmd.args.viewport.w;
+ v.Height = cmd.args.viewport.h;
+ v.MinDepth = cmd.args.viewport.d0;
+ v.MaxDepth = cmd.args.viewport.d1;
+ context->RSSetViewports(1, &v);
+ }
+ break;
+ case QD3D11CommandBuffer::Command::Scissor:
+ {
+ D3D11_RECT r;
+ r.left = cmd.args.scissor.x;
+ r.top = cmd.args.scissor.y;
+ // right and bottom are exclusive
+ r.right = cmd.args.scissor.x + cmd.args.scissor.w;
+ r.bottom = cmd.args.scissor.y + cmd.args.scissor.h;
+ context->RSSetScissorRects(1, &r);
+ }
+ break;
+ case QD3D11CommandBuffer::Command::BindVertexBuffers:
+ contextState.vsHighestActiveVertexBufferBinding = qMax<int>(
+ contextState.vsHighestActiveVertexBufferBinding,
+ cmd.args.bindVertexBuffers.startSlot + cmd.args.bindVertexBuffers.slotCount - 1);
+ context->IASetVertexBuffers(cmd.args.bindVertexBuffers.startSlot,
+ cmd.args.bindVertexBuffers.slotCount,
+ cmd.args.bindVertexBuffers.buffers,
+ cmd.args.bindVertexBuffers.strides,
+ cmd.args.bindVertexBuffers.offsets);
+ break;
+ case QD3D11CommandBuffer::Command::BindIndexBuffer:
+ contextState.vsHasIndexBufferBound = true;
+ context->IASetIndexBuffer(cmd.args.bindIndexBuffer.buffer,
+ cmd.args.bindIndexBuffer.format,
+ cmd.args.bindIndexBuffer.offset);
+ break;
+ case QD3D11CommandBuffer::Command::BindGraphicsPipeline:
+ {
+ QD3D11GraphicsPipeline *psD = cmd.args.bindGraphicsPipeline.ps;
+ context->VSSetShader(psD->vs, nullptr, 0);
+ context->PSSetShader(psD->fs, nullptr, 0);
+ context->IASetPrimitiveTopology(psD->d3dTopology);
+ context->IASetInputLayout(psD->inputLayout);
+ context->OMSetDepthStencilState(psD->dsState, stencilRef);
+ context->OMSetBlendState(psD->blendState, blendConstants, 0xffffffff);
+ context->RSSetState(psD->rastState);
+ }
+ break;
+ case QD3D11CommandBuffer::Command::BindShaderResources:
+ bindShaderResources(cmd.args.bindShaderResources.srb,
+ cmd.args.bindShaderResources.dynamicOffsetPairs,
+ cmd.args.bindShaderResources.dynamicOffsetCount,
+ cmd.args.bindShaderResources.offsetOnlyChange);
+ break;
+ case QD3D11CommandBuffer::Command::StencilRef:
+ stencilRef = cmd.args.stencilRef.ref;
+ context->OMSetDepthStencilState(cmd.args.stencilRef.ps->dsState, stencilRef);
+ break;
+ case QD3D11CommandBuffer::Command::BlendConstants:
+ memcpy(blendConstants, cmd.args.blendConstants.c, 4 * sizeof(float));
+ context->OMSetBlendState(cmd.args.blendConstants.ps->blendState, blendConstants, 0xffffffff);
+ break;
+ case QD3D11CommandBuffer::Command::Draw:
+ if (cmd.args.draw.ps) {
+ if (cmd.args.draw.instanceCount == 1)
+ context->Draw(cmd.args.draw.vertexCount, cmd.args.draw.firstVertex);
+ else
+ context->DrawInstanced(cmd.args.draw.vertexCount, cmd.args.draw.instanceCount,
+ cmd.args.draw.firstVertex, cmd.args.draw.firstInstance);
+ } else {
+ qWarning("No graphics pipeline active for draw; ignored");
+ }
+ break;
+ case QD3D11CommandBuffer::Command::DrawIndexed:
+ if (cmd.args.drawIndexed.ps) {
+ if (cmd.args.drawIndexed.instanceCount == 1)
+ context->DrawIndexed(cmd.args.drawIndexed.indexCount, cmd.args.drawIndexed.firstIndex,
+ cmd.args.drawIndexed.vertexOffset);
+ else
+ context->DrawIndexedInstanced(cmd.args.drawIndexed.indexCount, cmd.args.drawIndexed.instanceCount,
+ cmd.args.drawIndexed.firstIndex, cmd.args.drawIndexed.vertexOffset,
+ cmd.args.drawIndexed.firstInstance);
+ } else {
+ qWarning("No graphics pipeline active for drawIndexed; ignored");
+ }
+ break;
+ case QD3D11CommandBuffer::Command::UpdateSubRes:
+ context->UpdateSubresource(cmd.args.updateSubRes.dst, cmd.args.updateSubRes.dstSubRes,
+ cmd.args.updateSubRes.hasDstBox ? &cmd.args.updateSubRes.dstBox : nullptr,
+ cmd.args.updateSubRes.src, cmd.args.updateSubRes.srcRowPitch, 0);
+ break;
+ case QD3D11CommandBuffer::Command::CopySubRes:
+ context->CopySubresourceRegion(cmd.args.copySubRes.dst, cmd.args.copySubRes.dstSubRes,
+ cmd.args.copySubRes.dstX, cmd.args.copySubRes.dstY, 0,
+ cmd.args.copySubRes.src, cmd.args.copySubRes.srcSubRes,
+ cmd.args.copySubRes.hasSrcBox ? &cmd.args.copySubRes.srcBox : nullptr);
+ break;
+ case QD3D11CommandBuffer::Command::ResolveSubRes:
+ context->ResolveSubresource(cmd.args.resolveSubRes.dst, cmd.args.resolveSubRes.dstSubRes,
+ cmd.args.resolveSubRes.src, cmd.args.resolveSubRes.srcSubRes,
+ cmd.args.resolveSubRes.format);
+ break;
+ case QD3D11CommandBuffer::Command::GenMip:
+ context->GenerateMips(cmd.args.genMip.srv);
+ break;
+ case QD3D11CommandBuffer::Command::DebugMarkBegin:
+ annotations->BeginEvent(reinterpret_cast<LPCWSTR>(QString::fromLatin1(cmd.args.debugMark.s).utf16()));
+ break;
+ case QD3D11CommandBuffer::Command::DebugMarkEnd:
+ annotations->EndEvent();
+ break;
+ case QD3D11CommandBuffer::Command::DebugMarkMsg:
+ annotations->SetMarker(reinterpret_cast<LPCWSTR>(QString::fromLatin1(cmd.args.debugMark.s).utf16()));
+ break;
+ case QD3D11CommandBuffer::Command::BindComputePipeline:
+ context->CSSetShader(cmd.args.bindComputePipeline.ps->cs, nullptr, 0);
+ break;
+ case QD3D11CommandBuffer::Command::Dispatch:
+ context->Dispatch(cmd.args.dispatch.x, cmd.args.dispatch.y, cmd.args.dispatch.z);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+QD3D11Buffer::QD3D11Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size)
+ : QRhiBuffer(rhi, type, usage, size)
+{
+}
+
+QD3D11Buffer::~QD3D11Buffer()
+{
+ release();
+}
+
+void QD3D11Buffer::release()
+{
+ if (!buffer)
+ return;
+
+ dynBuf.clear();
+
+ buffer->Release();
+ buffer = nullptr;
+
+ if (uav) {
+ uav->Release();
+ uav = nullptr;
+ }
+
+ QRHI_RES_RHI(QRhiD3D11);
+ QRHI_PROF;
+ QRHI_PROF_F(releaseBuffer(this));
+ rhiD->unregisterResource(this);
+}
+
+static inline uint toD3DBufferUsage(QRhiBuffer::UsageFlags usage)
+{
+ int u = 0;
+ if (usage.testFlag(QRhiBuffer::VertexBuffer))
+ u |= D3D11_BIND_VERTEX_BUFFER;
+ if (usage.testFlag(QRhiBuffer::IndexBuffer))
+ u |= D3D11_BIND_INDEX_BUFFER;
+ if (usage.testFlag(QRhiBuffer::UniformBuffer))
+ u |= D3D11_BIND_CONSTANT_BUFFER;
+ if (usage.testFlag(QRhiBuffer::StorageBuffer))
+ u |= D3D11_BIND_UNORDERED_ACCESS;
+ return u;
+}
+
+bool QD3D11Buffer::build()
+{
+ if (buffer)
+ release();
+
+ if (m_usage.testFlag(QRhiBuffer::UniformBuffer) && m_type != Dynamic) {
+ qWarning("UniformBuffer must always be combined with Dynamic on D3D11");
+ return false;
+ }
+
+ if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) {
+ qWarning("StorageBuffer cannot be combined with Dynamic");
+ return false;
+ }
+
+ const int nonZeroSize = m_size <= 0 ? 256 : m_size;
+ const int roundedSize = aligned(nonZeroSize, m_usage.testFlag(QRhiBuffer::UniformBuffer) ? 256 : 4);
+
+ D3D11_BUFFER_DESC desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.ByteWidth = roundedSize;
+ desc.Usage = m_type == Dynamic ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
+ desc.BindFlags = toD3DBufferUsage(m_usage);
+ desc.CPUAccessFlags = m_type == Dynamic ? D3D11_CPU_ACCESS_WRITE : 0;
+ desc.MiscFlags = m_usage.testFlag(QRhiBuffer::StorageBuffer) ? D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS : 0;
+
+ QRHI_RES_RHI(QRhiD3D11);
+ HRESULT hr = rhiD->dev->CreateBuffer(&desc, nullptr, &buffer);
+ if (FAILED(hr)) {
+ qWarning("Failed to create buffer: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+
+ if (m_type == Dynamic) {
+ dynBuf.resize(m_size);
+ hasPendingDynamicUpdates = false;
+ }
+
+ if (!m_objectName.isEmpty())
+ buffer->SetPrivateData(WKPDID_D3DDebugObjectName, m_objectName.size(), m_objectName.constData());
+
+ QRHI_PROF;
+ QRHI_PROF_F(newBuffer(this, roundedSize, m_type == Dynamic ? 2 : 1, m_type == Dynamic ? 1 : 0));
+
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+ID3D11UnorderedAccessView *QD3D11Buffer::unorderedAccessView()
+{
+ if (uav)
+ return uav;
+
+ // SPIRV-Cross generated HLSL uses RWByteAddressBuffer
+ D3D11_UNORDERED_ACCESS_VIEW_DESC desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.Format = DXGI_FORMAT_R32_TYPELESS;
+ desc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
+ desc.Buffer.FirstElement = 0;
+ desc.Buffer.NumElements = aligned(m_size, 4) / 4;
+ desc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_RAW;
+
+ QRHI_RES_RHI(QRhiD3D11);
+ HRESULT hr = rhiD->dev->CreateUnorderedAccessView(buffer, &desc, &uav);
+ if (FAILED(hr)) {
+ qWarning("Failed to create UAV: %s", qPrintable(comErrorMessage(hr)));
+ return nullptr;
+ }
+
+ return uav;
+}
+
+QD3D11RenderBuffer::QD3D11RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags)
+ : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags)
+{
+}
+
+QD3D11RenderBuffer::~QD3D11RenderBuffer()
+{
+ release();
+}
+
+void QD3D11RenderBuffer::release()
+{
+ if (!tex)
+ return;
+
+ if (dsv) {
+ dsv->Release();
+ dsv = nullptr;
+ }
+
+ if (rtv) {
+ rtv->Release();
+ rtv = nullptr;
+ }
+
+ tex->Release();
+ tex = nullptr;
+
+ QRHI_RES_RHI(QRhiD3D11);
+ QRHI_PROF;
+ QRHI_PROF_F(releaseRenderBuffer(this));
+ rhiD->unregisterResource(this);
+}
+
+bool QD3D11RenderBuffer::build()
+{
+ if (tex)
+ release();
+
+ if (m_pixelSize.isEmpty())
+ return false;
+
+ QRHI_RES_RHI(QRhiD3D11);
+ sampleDesc = rhiD->effectiveSampleCount(m_sampleCount);
+
+ D3D11_TEXTURE2D_DESC desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.Width = m_pixelSize.width();
+ desc.Height = m_pixelSize.height();
+ desc.MipLevels = 1;
+ desc.ArraySize = 1;
+ desc.SampleDesc = sampleDesc;
+ desc.Usage = D3D11_USAGE_DEFAULT;
+
+ if (m_type == Color) {
+ dxgiFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
+ desc.Format = dxgiFormat;
+ desc.BindFlags = D3D11_BIND_RENDER_TARGET;
+ HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, &tex);
+ if (FAILED(hr)) {
+ qWarning("Failed to create color renderbuffer: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
+ memset(&rtvDesc, 0, sizeof(rtvDesc));
+ rtvDesc.Format = dxgiFormat;
+ rtvDesc.ViewDimension = desc.SampleDesc.Count > 1 ? D3D11_RTV_DIMENSION_TEXTURE2DMS
+ : D3D11_RTV_DIMENSION_TEXTURE2D;
+ hr = rhiD->dev->CreateRenderTargetView(tex, &rtvDesc, &rtv);
+ if (FAILED(hr)) {
+ qWarning("Failed to create rtv: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ } else if (m_type == DepthStencil) {
+ dxgiFormat = DXGI_FORMAT_D24_UNORM_S8_UINT;
+ desc.Format = dxgiFormat;
+ desc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
+ HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, &tex);
+ if (FAILED(hr)) {
+ qWarning("Failed to create depth-stencil buffer: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc;
+ memset(&dsvDesc, 0, sizeof(dsvDesc));
+ dsvDesc.Format = dxgiFormat;
+ dsvDesc.ViewDimension = desc.SampleDesc.Count > 1 ? D3D11_DSV_DIMENSION_TEXTURE2DMS
+ : D3D11_DSV_DIMENSION_TEXTURE2D;
+ hr = rhiD->dev->CreateDepthStencilView(tex, &dsvDesc, &dsv);
+ if (FAILED(hr)) {
+ qWarning("Failed to create dsv: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ } else {
+ return false;
+ }
+
+ if (!m_objectName.isEmpty())
+ tex->SetPrivateData(WKPDID_D3DDebugObjectName, m_objectName.size(), m_objectName.constData());
+
+ QRHI_PROF;
+ QRHI_PROF_F(newRenderBuffer(this, false, false, sampleDesc.Count));
+
+ rhiD->registerResource(this);
+ return true;
+}
+
+QRhiTexture::Format QD3D11RenderBuffer::backingFormat() const
+{
+ return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
+}
+
+QD3D11Texture::QD3D11Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+ int sampleCount, Flags flags)
+ : QRhiTexture(rhi, format, pixelSize, sampleCount, flags)
+{
+ for (int i = 0; i < QRhi::MAX_LEVELS; ++i)
+ perLevelViews[i] = nullptr;
+}
+
+QD3D11Texture::~QD3D11Texture()
+{
+ release();
+}
+
+void QD3D11Texture::release()
+{
+ if (!tex)
+ return;
+
+ if (srv) {
+ srv->Release();
+ srv = nullptr;
+ }
+
+ for (int i = 0; i < QRhi::MAX_LEVELS; ++i) {
+ if (perLevelViews[i]) {
+ perLevelViews[i]->Release();
+ perLevelViews[i] = nullptr;
+ }
+ }
+
+ if (owns)
+ tex->Release();
+
+ tex = nullptr;
+
+ QRHI_RES_RHI(QRhiD3D11);
+ QRHI_PROF;
+ QRHI_PROF_F(releaseTexture(this));
+ rhiD->unregisterResource(this);
+}
+
+static inline DXGI_FORMAT toD3DDepthTextureSRVFormat(QRhiTexture::Format format)
+{
+ switch (format) {
+ case QRhiTexture::Format::D16:
+ return DXGI_FORMAT_R16_FLOAT;
+ case QRhiTexture::Format::D32F:
+ return DXGI_FORMAT_R32_FLOAT;
+ default:
+ Q_UNREACHABLE();
+ return DXGI_FORMAT_R32_FLOAT;
+ }
+}
+
+static inline DXGI_FORMAT toD3DDepthTextureDSVFormat(QRhiTexture::Format format)
+{
+ switch (format) {
+ case QRhiTexture::Format::D16:
+ return DXGI_FORMAT_D16_UNORM;
+ case QRhiTexture::Format::D32F:
+ return DXGI_FORMAT_D32_FLOAT;
+ default:
+ Q_UNREACHABLE();
+ return DXGI_FORMAT_D32_FLOAT;
+ }
+}
+
+bool QD3D11Texture::prepareBuild(QSize *adjustedSize)
+{
+ if (tex)
+ release();
+
+ const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
+ const bool isDepth = isDepthTextureFormat(m_format);
+ const bool isCube = m_flags.testFlag(CubeMap);
+ const bool hasMipMaps = m_flags.testFlag(MipMapped);
+
+ QRHI_RES_RHI(QRhiD3D11);
+ dxgiFormat = toD3DTextureFormat(m_format, m_flags);
+ mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
+ sampleDesc = rhiD->effectiveSampleCount(m_sampleCount);
+ if (sampleDesc.Count > 1) {
+ if (isCube) {
+ qWarning("Cubemap texture cannot be multisample");
+ return false;
+ }
+ if (hasMipMaps) {
+ qWarning("Multisample texture cannot have mipmaps");
+ return false;
+ }
+ }
+ if (isDepth && hasMipMaps) {
+ qWarning("Depth texture cannot have mipmaps");
+ return false;
+ }
+
+ if (adjustedSize)
+ *adjustedSize = size;
+
+ return true;
+}
+
+bool QD3D11Texture::finishBuild()
+{
+ QRHI_RES_RHI(QRhiD3D11);
+ const bool isDepth = isDepthTextureFormat(m_format);
+ const bool isCube = m_flags.testFlag(CubeMap);
+
+ D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
+ memset(&srvDesc, 0, sizeof(srvDesc));
+ srvDesc.Format = isDepth ? toD3DDepthTextureSRVFormat(m_format) : dxgiFormat;
+ if (isCube) {
+ srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
+ srvDesc.TextureCube.MipLevels = mipLevelCount;
+ } else {
+ if (sampleDesc.Count > 1) {
+ srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMS;
+ } else {
+ srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
+ srvDesc.Texture2D.MipLevels = mipLevelCount;
+ }
+ }
+
+ HRESULT hr = rhiD->dev->CreateShaderResourceView(tex, &srvDesc, &srv);
+ if (FAILED(hr)) {
+ qWarning("Failed to create srv: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+
+ nativeHandlesStruct.texture = tex;
+
+ generation += 1;
+ return true;
+}
+
+bool QD3D11Texture::build()
+{
+ QSize size;
+ if (!prepareBuild(&size))
+ return false;
+
+ const bool isDepth = isDepthTextureFormat(m_format);
+ const bool isCube = m_flags.testFlag(CubeMap);
+
+ uint bindFlags = D3D11_BIND_SHADER_RESOURCE;
+ uint miscFlags = isCube ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0;
+ if (m_flags.testFlag(RenderTarget)) {
+ if (isDepth)
+ bindFlags |= D3D11_BIND_DEPTH_STENCIL;
+ else
+ bindFlags |= D3D11_BIND_RENDER_TARGET;
+ }
+ if (m_flags.testFlag(UsedWithGenerateMips)) {
+ if (isDepth) {
+ qWarning("Depth texture cannot have mipmaps generated");
+ return false;
+ }
+ bindFlags |= D3D11_BIND_RENDER_TARGET;
+ miscFlags |= D3D11_RESOURCE_MISC_GENERATE_MIPS;
+ }
+ if (m_flags.testFlag(UsedWithLoadStore))
+ bindFlags |= D3D11_BIND_UNORDERED_ACCESS;
+
+ D3D11_TEXTURE2D_DESC desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.Width = size.width();
+ desc.Height = size.height();
+ desc.MipLevels = mipLevelCount;
+ desc.ArraySize = isCube ? 6 : 1;
+ desc.Format = dxgiFormat;
+ desc.SampleDesc = sampleDesc;
+ desc.Usage = D3D11_USAGE_DEFAULT;
+ desc.BindFlags = bindFlags;
+ desc.MiscFlags = miscFlags;
+
+ QRHI_RES_RHI(QRhiD3D11);
+ HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, &tex);
+ if (FAILED(hr)) {
+ qWarning("Failed to create texture: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+
+ if (!finishBuild())
+ return false;
+
+ if (!m_objectName.isEmpty())
+ tex->SetPrivateData(WKPDID_D3DDebugObjectName, m_objectName.size(), m_objectName.constData());
+
+ QRHI_PROF;
+ QRHI_PROF_F(newTexture(this, true, mipLevelCount, isCube ? 6 : 1, sampleDesc.Count));
+
+ owns = true;
+ rhiD->registerResource(this);
+ return true;
+}
+
+bool QD3D11Texture::buildFrom(const QRhiNativeHandles *src)
+{
+ const QRhiD3D11TextureNativeHandles *h = static_cast<const QRhiD3D11TextureNativeHandles *>(src);
+ if (!h || !h->texture)
+ return false;
+
+ if (!prepareBuild())
+ return false;
+
+ tex = static_cast<ID3D11Texture2D *>(h->texture);
+
+ if (!finishBuild())
+ return false;
+
+ QRHI_PROF;
+ QRHI_PROF_F(newTexture(this, false, mipLevelCount, m_flags.testFlag(CubeMap) ? 6 : 1, sampleDesc.Count));
+
+ owns = false;
+ QRHI_RES_RHI(QRhiD3D11);
+ rhiD->registerResource(this);
+ return true;
+}
+
+const QRhiNativeHandles *QD3D11Texture::nativeHandles()
+{
+ return &nativeHandlesStruct;
+}
+
+ID3D11UnorderedAccessView *QD3D11Texture::unorderedAccessViewForLevel(int level)
+{
+ if (perLevelViews[level])
+ return perLevelViews[level];
+
+ const bool isCube = m_flags.testFlag(CubeMap);
+ D3D11_UNORDERED_ACCESS_VIEW_DESC desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.Format = dxgiFormat;
+ if (isCube) {
+ desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2DARRAY;
+ desc.Texture2DArray.MipSlice = level;
+ desc.Texture2DArray.FirstArraySlice = 0;
+ desc.Texture2DArray.ArraySize = 6;
+ } else {
+ desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D;
+ desc.Texture2D.MipSlice = level;
+ }
+
+ QRHI_RES_RHI(QRhiD3D11);
+ ID3D11UnorderedAccessView *uav = nullptr;
+ HRESULT hr = rhiD->dev->CreateUnorderedAccessView(tex, &desc, &uav);
+ if (FAILED(hr)) {
+ qWarning("Failed to create UAV: %s", qPrintable(comErrorMessage(hr)));
+ return nullptr;
+ }
+
+ perLevelViews[level] = uav;
+ return uav;
+}
+
+QD3D11Sampler::QD3D11Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
+ AddressMode u, AddressMode v)
+ : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v)
+{
+}
+
+QD3D11Sampler::~QD3D11Sampler()
+{
+ release();
+}
+
+void QD3D11Sampler::release()
+{
+ if (!samplerState)
+ return;
+
+ samplerState->Release();
+ samplerState = nullptr;
+
+ QRHI_RES_RHI(QRhiD3D11);
+ rhiD->unregisterResource(this);
+}
+
+static inline D3D11_FILTER toD3DFilter(QRhiSampler::Filter minFilter, QRhiSampler::Filter magFilter, QRhiSampler::Filter mipFilter)
+{
+ if (minFilter == QRhiSampler::Nearest) {
+ if (magFilter == QRhiSampler::Nearest) {
+ if (mipFilter == QRhiSampler::Linear)
+ return D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR;
+ else
+ return D3D11_FILTER_MIN_MAG_MIP_POINT;
+ } else {
+ if (mipFilter == QRhiSampler::Linear)
+ return D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR;
+ else
+ return D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT;
+ }
+ } else {
+ if (magFilter == QRhiSampler::Nearest) {
+ if (mipFilter == QRhiSampler::Linear)
+ return D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR;
+ else
+ return D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT;
+ } else {
+ if (mipFilter == QRhiSampler::Linear)
+ return D3D11_FILTER_MIN_MAG_MIP_LINEAR;
+ else
+ return D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;
+ }
+ }
+
+ Q_UNREACHABLE();
+ return D3D11_FILTER_MIN_MAG_MIP_LINEAR;
+}
+
+static inline D3D11_TEXTURE_ADDRESS_MODE toD3DAddressMode(QRhiSampler::AddressMode m)
+{
+ switch (m) {
+ case QRhiSampler::Repeat:
+ return D3D11_TEXTURE_ADDRESS_WRAP;
+ case QRhiSampler::ClampToEdge:
+ return D3D11_TEXTURE_ADDRESS_CLAMP;
+ case QRhiSampler::Border:
+ return D3D11_TEXTURE_ADDRESS_BORDER;
+ case QRhiSampler::Mirror:
+ return D3D11_TEXTURE_ADDRESS_MIRROR;
+ case QRhiSampler::MirrorOnce:
+ return D3D11_TEXTURE_ADDRESS_MIRROR_ONCE;
+ default:
+ Q_UNREACHABLE();
+ return D3D11_TEXTURE_ADDRESS_CLAMP;
+ }
+}
+
+static inline D3D11_COMPARISON_FUNC toD3DTextureComparisonFunc(QRhiSampler::CompareOp op)
+{
+ switch (op) {
+ case QRhiSampler::Never:
+ return D3D11_COMPARISON_NEVER;
+ case QRhiSampler::Less:
+ return D3D11_COMPARISON_LESS;
+ case QRhiSampler::Equal:
+ return D3D11_COMPARISON_EQUAL;
+ case QRhiSampler::LessOrEqual:
+ return D3D11_COMPARISON_LESS_EQUAL;
+ case QRhiSampler::Greater:
+ return D3D11_COMPARISON_GREATER;
+ case QRhiSampler::NotEqual:
+ return D3D11_COMPARISON_NOT_EQUAL;
+ case QRhiSampler::GreaterOrEqual:
+ return D3D11_COMPARISON_GREATER_EQUAL;
+ case QRhiSampler::Always:
+ return D3D11_COMPARISON_ALWAYS;
+ default:
+ Q_UNREACHABLE();
+ return D3D11_COMPARISON_NEVER;
+ }
+}
+
+bool QD3D11Sampler::build()
+{
+ if (samplerState)
+ release();
+
+ D3D11_SAMPLER_DESC desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.Filter = toD3DFilter(m_minFilter, m_magFilter, m_mipmapMode);
+ if (m_compareOp != Never)
+ desc.Filter = D3D11_FILTER(desc.Filter | 0x80);
+ desc.AddressU = toD3DAddressMode(m_addressU);
+ desc.AddressV = toD3DAddressMode(m_addressV);
+ desc.AddressW = toD3DAddressMode(m_addressW);
+ desc.MaxAnisotropy = 1.0f;
+ desc.ComparisonFunc = toD3DTextureComparisonFunc(m_compareOp);
+ desc.MaxLOD = m_mipmapMode == None ? 0.0f : 1000.0f;
+
+ QRHI_RES_RHI(QRhiD3D11);
+ HRESULT hr = rhiD->dev->CreateSamplerState(&desc, &samplerState);
+ if (FAILED(hr)) {
+ qWarning("Failed to create sampler state: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+// dummy, no Vulkan-style RenderPass+Framebuffer concept here
+QD3D11RenderPassDescriptor::QD3D11RenderPassDescriptor(QRhiImplementation *rhi)
+ : QRhiRenderPassDescriptor(rhi)
+{
+}
+
+QD3D11RenderPassDescriptor::~QD3D11RenderPassDescriptor()
+{
+ release();
+}
+
+void QD3D11RenderPassDescriptor::release()
+{
+ // nothing to do here
+}
+
+QD3D11ReferenceRenderTarget::QD3D11ReferenceRenderTarget(QRhiImplementation *rhi)
+ : QRhiRenderTarget(rhi),
+ d(rhi)
+{
+}
+
+QD3D11ReferenceRenderTarget::~QD3D11ReferenceRenderTarget()
+{
+ release();
+}
+
+void QD3D11ReferenceRenderTarget::release()
+{
+ // nothing to do here
+}
+
+QSize QD3D11ReferenceRenderTarget::pixelSize() const
+{
+ return d.pixelSize;
+}
+
+float QD3D11ReferenceRenderTarget::devicePixelRatio() const
+{
+ return d.dpr;
+}
+
+int QD3D11ReferenceRenderTarget::sampleCount() const
+{
+ return d.sampleCount;
+}
+
+QD3D11TextureRenderTarget::QD3D11TextureRenderTarget(QRhiImplementation *rhi,
+ const QRhiTextureRenderTargetDescription &desc,
+ Flags flags)
+ : QRhiTextureRenderTarget(rhi, desc, flags),
+ d(rhi)
+{
+ for (int i = 0; i < QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS; ++i) {
+ ownsRtv[i] = false;
+ rtv[i] = nullptr;
+ }
+}
+
+QD3D11TextureRenderTarget::~QD3D11TextureRenderTarget()
+{
+ release();
+}
+
+void QD3D11TextureRenderTarget::release()
+{
+ QRHI_RES_RHI(QRhiD3D11);
+
+ if (!rtv[0] && !dsv)
+ return;
+
+ if (dsv) {
+ if (ownsDsv)
+ dsv->Release();
+ dsv = nullptr;
+ }
+
+ for (int i = 0; i < QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS; ++i) {
+ if (rtv[i]) {
+ if (ownsRtv[i])
+ rtv[i]->Release();
+ rtv[i] = nullptr;
+ }
+ }
+
+ rhiD->unregisterResource(this);
+}
+
+QRhiRenderPassDescriptor *QD3D11TextureRenderTarget::newCompatibleRenderPassDescriptor()
+{
+ return new QD3D11RenderPassDescriptor(m_rhi);
+}
+
+bool QD3D11TextureRenderTarget::build()
+{
+ if (rtv[0] || dsv)
+ release();
+
+ const QVector<QRhiColorAttachment> colorAttachments = m_desc.colorAttachments();
+ Q_ASSERT(!colorAttachments.isEmpty() || m_desc.depthTexture());
+ Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
+ const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
+
+ QRHI_RES_RHI(QRhiD3D11);
+
+ d.colorAttCount = colorAttachments.count();
+ for (int i = 0; i < d.colorAttCount; ++i) {
+ QRhiTexture *texture = colorAttachments[i].texture();
+ QRhiRenderBuffer *rb = colorAttachments[i].renderBuffer();
+ Q_ASSERT(texture || rb);
+ if (texture) {
+ QD3D11Texture *texD = QRHI_RES(QD3D11Texture, texture);
+ D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
+ memset(&rtvDesc, 0, sizeof(rtvDesc));
+ rtvDesc.Format = toD3DTextureFormat(texD->format(), texD->flags());
+ if (texD->flags().testFlag(QRhiTexture::CubeMap)) {
+ rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
+ rtvDesc.Texture2DArray.MipSlice = colorAttachments[i].level();
+ rtvDesc.Texture2DArray.FirstArraySlice = colorAttachments[i].layer();
+ rtvDesc.Texture2DArray.ArraySize = 1;
+ } else {
+ if (texD->sampleDesc.Count > 1) {
+ rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS;
+ } else {
+ rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
+ rtvDesc.Texture2D.MipSlice = colorAttachments[i].level();
+ }
+ }
+ HRESULT hr = rhiD->dev->CreateRenderTargetView(texD->tex, &rtvDesc, &rtv[i]);
+ if (FAILED(hr)) {
+ qWarning("Failed to create rtv: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ ownsRtv[i] = true;
+ if (i == 0) {
+ d.pixelSize = texD->pixelSize();
+ d.sampleCount = texD->sampleDesc.Count;
+ }
+ } else if (rb) {
+ QD3D11RenderBuffer *rbD = QRHI_RES(QD3D11RenderBuffer, rb);
+ ownsRtv[i] = false;
+ rtv[i] = rbD->rtv;
+ if (i == 0) {
+ d.pixelSize = rbD->pixelSize();
+ d.sampleCount = rbD->sampleDesc.Count;
+ }
+ }
+ }
+ d.dpr = 1;
+
+ if (hasDepthStencil) {
+ if (m_desc.depthTexture()) {
+ ownsDsv = true;
+ QD3D11Texture *depthTexD = QRHI_RES(QD3D11Texture, m_desc.depthTexture());
+ D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc;
+ memset(&dsvDesc, 0, sizeof(dsvDesc));
+ dsvDesc.Format = toD3DDepthTextureDSVFormat(depthTexD->format());
+ dsvDesc.ViewDimension = depthTexD->sampleDesc.Count > 1 ? D3D11_DSV_DIMENSION_TEXTURE2DMS
+ : D3D11_DSV_DIMENSION_TEXTURE2D;
+ HRESULT hr = rhiD->dev->CreateDepthStencilView(depthTexD->tex, &dsvDesc, &dsv);
+ if (FAILED(hr)) {
+ qWarning("Failed to create dsv: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ if (d.colorAttCount == 0) {
+ d.pixelSize = depthTexD->pixelSize();
+ d.sampleCount = depthTexD->sampleDesc.Count;
+ }
+ } else {
+ ownsDsv = false;
+ QD3D11RenderBuffer *depthRbD = QRHI_RES(QD3D11RenderBuffer, m_desc.depthStencilBuffer());
+ dsv = depthRbD->dsv;
+ if (d.colorAttCount == 0) {
+ d.pixelSize = m_desc.depthStencilBuffer()->pixelSize();
+ d.sampleCount = depthRbD->sampleDesc.Count;
+ }
+ }
+ d.dsAttCount = 1;
+ } else {
+ d.dsAttCount = 0;
+ }
+
+ for (int i = 0; i < QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS; ++i)
+ d.rtv[i] = i < d.colorAttCount ? rtv[i] : nullptr;
+
+ d.dsv = dsv;
+ d.rp = QRHI_RES(QD3D11RenderPassDescriptor, m_renderPassDesc);
+
+ rhiD->registerResource(this);
+ return true;
+}
+
+QSize QD3D11TextureRenderTarget::pixelSize() const
+{
+ return d.pixelSize;
+}
+
+float QD3D11TextureRenderTarget::devicePixelRatio() const
+{
+ return d.dpr;
+}
+
+int QD3D11TextureRenderTarget::sampleCount() const
+{
+ return d.sampleCount;
+}
+
+QD3D11ShaderResourceBindings::QD3D11ShaderResourceBindings(QRhiImplementation *rhi)
+ : QRhiShaderResourceBindings(rhi)
+{
+}
+
+QD3D11ShaderResourceBindings::~QD3D11ShaderResourceBindings()
+{
+ release();
+}
+
+void QD3D11ShaderResourceBindings::release()
+{
+ sortedBindings.clear();
+}
+
+bool QD3D11ShaderResourceBindings::build()
+{
+ if (!sortedBindings.isEmpty())
+ release();
+
+ sortedBindings = m_bindings;
+ std::sort(sortedBindings.begin(), sortedBindings.end(),
+ [](const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b)
+ {
+ return QRhiShaderResourceBindingPrivate::get(&a)->binding < QRhiShaderResourceBindingPrivate::get(&b)->binding;
+ });
+
+ boundResourceData.resize(sortedBindings.count());
+
+ QRHI_RES_RHI(QRhiD3D11);
+ rhiD->updateShaderResourceBindings(this);
+
+ generation += 1;
+ return true;
+}
+
+QD3D11GraphicsPipeline::QD3D11GraphicsPipeline(QRhiImplementation *rhi)
+ : QRhiGraphicsPipeline(rhi)
+{
+}
+
+QD3D11GraphicsPipeline::~QD3D11GraphicsPipeline()
+{
+ release();
+}
+
+void QD3D11GraphicsPipeline::release()
+{
+ QRHI_RES_RHI(QRhiD3D11);
+
+ if (!dsState)
+ return;
+
+ dsState->Release();
+ dsState = nullptr;
+
+ if (blendState) {
+ blendState->Release();
+ blendState = nullptr;
+ }
+
+ if (inputLayout) {
+ inputLayout->Release();
+ inputLayout = nullptr;
+ }
+
+ if (rastState) {
+ rastState->Release();
+ rastState = nullptr;
+ }
+
+ if (vs) {
+ vs->Release();
+ vs = nullptr;
+ }
+
+ if (fs) {
+ fs->Release();
+ fs = nullptr;
+ }
+
+ rhiD->unregisterResource(this);
+}
+
+static inline D3D11_CULL_MODE toD3DCullMode(QRhiGraphicsPipeline::CullMode c)
+{
+ switch (c) {
+ case QRhiGraphicsPipeline::None:
+ return D3D11_CULL_NONE;
+ case QRhiGraphicsPipeline::Front:
+ return D3D11_CULL_FRONT;
+ case QRhiGraphicsPipeline::Back:
+ return D3D11_CULL_BACK;
+ default:
+ Q_UNREACHABLE();
+ return D3D11_CULL_NONE;
+ }
+}
+
+static inline D3D11_COMPARISON_FUNC toD3DCompareOp(QRhiGraphicsPipeline::CompareOp op)
+{
+ switch (op) {
+ case QRhiGraphicsPipeline::Never:
+ return D3D11_COMPARISON_NEVER;
+ case QRhiGraphicsPipeline::Less:
+ return D3D11_COMPARISON_LESS;
+ case QRhiGraphicsPipeline::Equal:
+ return D3D11_COMPARISON_EQUAL;
+ case QRhiGraphicsPipeline::LessOrEqual:
+ return D3D11_COMPARISON_LESS_EQUAL;
+ case QRhiGraphicsPipeline::Greater:
+ return D3D11_COMPARISON_GREATER;
+ case QRhiGraphicsPipeline::NotEqual:
+ return D3D11_COMPARISON_NOT_EQUAL;
+ case QRhiGraphicsPipeline::GreaterOrEqual:
+ return D3D11_COMPARISON_GREATER_EQUAL;
+ case QRhiGraphicsPipeline::Always:
+ return D3D11_COMPARISON_ALWAYS;
+ default:
+ Q_UNREACHABLE();
+ return D3D11_COMPARISON_ALWAYS;
+ }
+}
+
+static inline D3D11_STENCIL_OP toD3DStencilOp(QRhiGraphicsPipeline::StencilOp op)
+{
+ switch (op) {
+ case QRhiGraphicsPipeline::StencilZero:
+ return D3D11_STENCIL_OP_ZERO;
+ case QRhiGraphicsPipeline::Keep:
+ return D3D11_STENCIL_OP_KEEP;
+ case QRhiGraphicsPipeline::Replace:
+ return D3D11_STENCIL_OP_REPLACE;
+ case QRhiGraphicsPipeline::IncrementAndClamp:
+ return D3D11_STENCIL_OP_INCR_SAT;
+ case QRhiGraphicsPipeline::DecrementAndClamp:
+ return D3D11_STENCIL_OP_DECR_SAT;
+ case QRhiGraphicsPipeline::Invert:
+ return D3D11_STENCIL_OP_INVERT;
+ case QRhiGraphicsPipeline::IncrementAndWrap:
+ return D3D11_STENCIL_OP_INCR;
+ case QRhiGraphicsPipeline::DecrementAndWrap:
+ return D3D11_STENCIL_OP_DECR;
+ default:
+ Q_UNREACHABLE();
+ return D3D11_STENCIL_OP_KEEP;
+ }
+}
+
+static inline DXGI_FORMAT toD3DAttributeFormat(QRhiVertexInputAttribute::Format format)
+{
+ switch (format) {
+ case QRhiVertexInputAttribute::Float4:
+ return DXGI_FORMAT_R32G32B32A32_FLOAT;
+ case QRhiVertexInputAttribute::Float3:
+ return DXGI_FORMAT_R32G32B32_FLOAT;
+ case QRhiVertexInputAttribute::Float2:
+ return DXGI_FORMAT_R32G32_FLOAT;
+ case QRhiVertexInputAttribute::Float:
+ return DXGI_FORMAT_R32_FLOAT;
+ case QRhiVertexInputAttribute::UNormByte4:
+ return DXGI_FORMAT_R8G8B8A8_UNORM;
+ case QRhiVertexInputAttribute::UNormByte2:
+ return DXGI_FORMAT_R8G8_UNORM;
+ case QRhiVertexInputAttribute::UNormByte:
+ return DXGI_FORMAT_R8_UNORM;
+ default:
+ Q_UNREACHABLE();
+ return DXGI_FORMAT_R32G32B32A32_FLOAT;
+ }
+}
+
+static inline D3D11_PRIMITIVE_TOPOLOGY toD3DTopology(QRhiGraphicsPipeline::Topology t)
+{
+ switch (t) {
+ case QRhiGraphicsPipeline::Triangles:
+ return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
+ case QRhiGraphicsPipeline::TriangleStrip:
+ return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
+ case QRhiGraphicsPipeline::Lines:
+ return D3D11_PRIMITIVE_TOPOLOGY_LINELIST;
+ case QRhiGraphicsPipeline::LineStrip:
+ return D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP;
+ case QRhiGraphicsPipeline::Points:
+ return D3D11_PRIMITIVE_TOPOLOGY_POINTLIST;
+ default:
+ Q_UNREACHABLE();
+ return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
+ }
+}
+
+static inline uint toD3DColorWriteMask(QRhiGraphicsPipeline::ColorMask c)
+{
+ uint f = 0;
+ if (c.testFlag(QRhiGraphicsPipeline::R))
+ f |= D3D11_COLOR_WRITE_ENABLE_RED;
+ if (c.testFlag(QRhiGraphicsPipeline::G))
+ f |= D3D11_COLOR_WRITE_ENABLE_GREEN;
+ if (c.testFlag(QRhiGraphicsPipeline::B))
+ f |= D3D11_COLOR_WRITE_ENABLE_BLUE;
+ if (c.testFlag(QRhiGraphicsPipeline::A))
+ f |= D3D11_COLOR_WRITE_ENABLE_ALPHA;
+ return f;
+}
+
+static inline D3D11_BLEND toD3DBlendFactor(QRhiGraphicsPipeline::BlendFactor f)
+{
+ switch (f) {
+ case QRhiGraphicsPipeline::Zero:
+ return D3D11_BLEND_ZERO;
+ case QRhiGraphicsPipeline::One:
+ return D3D11_BLEND_ONE;
+ case QRhiGraphicsPipeline::SrcColor:
+ return D3D11_BLEND_SRC_COLOR;
+ case QRhiGraphicsPipeline::OneMinusSrcColor:
+ return D3D11_BLEND_INV_SRC_COLOR;
+ case QRhiGraphicsPipeline::DstColor:
+ return D3D11_BLEND_DEST_COLOR;
+ case QRhiGraphicsPipeline::OneMinusDstColor:
+ return D3D11_BLEND_INV_DEST_COLOR;
+ case QRhiGraphicsPipeline::SrcAlpha:
+ return D3D11_BLEND_SRC_ALPHA;
+ case QRhiGraphicsPipeline::OneMinusSrcAlpha:
+ return D3D11_BLEND_INV_SRC_ALPHA;
+ case QRhiGraphicsPipeline::DstAlpha:
+ return D3D11_BLEND_DEST_ALPHA;
+ case QRhiGraphicsPipeline::OneMinusDstAlpha:
+ return D3D11_BLEND_INV_DEST_ALPHA;
+ case QRhiGraphicsPipeline::ConstantColor:
+ Q_FALLTHROUGH();
+ case QRhiGraphicsPipeline::ConstantAlpha:
+ return D3D11_BLEND_BLEND_FACTOR;
+ case QRhiGraphicsPipeline::OneMinusConstantColor:
+ Q_FALLTHROUGH();
+ case QRhiGraphicsPipeline::OneMinusConstantAlpha:
+ return D3D11_BLEND_INV_BLEND_FACTOR;
+ case QRhiGraphicsPipeline::SrcAlphaSaturate:
+ return D3D11_BLEND_SRC_ALPHA_SAT;
+ case QRhiGraphicsPipeline::Src1Color:
+ return D3D11_BLEND_SRC1_COLOR;
+ case QRhiGraphicsPipeline::OneMinusSrc1Color:
+ return D3D11_BLEND_INV_SRC1_COLOR;
+ case QRhiGraphicsPipeline::Src1Alpha:
+ return D3D11_BLEND_SRC1_ALPHA;
+ case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
+ return D3D11_BLEND_INV_SRC1_ALPHA;
+ default:
+ Q_UNREACHABLE();
+ return D3D11_BLEND_ZERO;
+ }
+}
+
+static inline D3D11_BLEND_OP toD3DBlendOp(QRhiGraphicsPipeline::BlendOp op)
+{
+ switch (op) {
+ case QRhiGraphicsPipeline::Add:
+ return D3D11_BLEND_OP_ADD;
+ case QRhiGraphicsPipeline::Subtract:
+ return D3D11_BLEND_OP_SUBTRACT;
+ case QRhiGraphicsPipeline::ReverseSubtract:
+ return D3D11_BLEND_OP_REV_SUBTRACT;
+ case QRhiGraphicsPipeline::Min:
+ return D3D11_BLEND_OP_MIN;
+ case QRhiGraphicsPipeline::Max:
+ return D3D11_BLEND_OP_MAX;
+ default:
+ Q_UNREACHABLE();
+ return D3D11_BLEND_OP_ADD;
+ }
+}
+
+static QByteArray compileHlslShaderSource(const QShader &shader, QShader::Variant shaderVariant, QString *error)
+{
+ QShaderCode dxbc = shader.shader({ QShader::DxbcShader, 50, shaderVariant });
+ if (!dxbc.shader().isEmpty())
+ return dxbc.shader();
+
+ QShaderCode hlslSource = shader.shader({ QShader::HlslShader, 50, shaderVariant });
+ if (hlslSource.shader().isEmpty()) {
+ qWarning() << "No HLSL (shader model 5.0) code found in baked shader" << shader;
+ return QByteArray();
+ }
+
+ const char *target;
+ switch (shader.stage()) {
+ case QShader::VertexStage:
+ target = "vs_5_0";
+ break;
+ case QShader::TessellationControlStage:
+ target = "hs_5_0";
+ break;
+ case QShader::TessellationEvaluationStage:
+ target = "ds_5_0";
+ break;
+ case QShader::GeometryStage:
+ target = "gs_5_0";
+ break;
+ case QShader::FragmentStage:
+ target = "ps_5_0";
+ break;
+ case QShader::ComputeStage:
+ target = "cs_5_0";
+ break;
+ default:
+ Q_UNREACHABLE();
+ return QByteArray();
+ }
+
+ ID3DBlob *bytecode = nullptr;
+ ID3DBlob *errors = nullptr;
+ HRESULT hr = D3DCompile(hlslSource.shader().constData(), hlslSource.shader().size(),
+ nullptr, nullptr, nullptr,
+ hlslSource.entryPoint().constData(), target, 0, 0, &bytecode, &errors);
+ if (FAILED(hr) || !bytecode) {
+ qWarning("HLSL shader compilation failed: 0x%x", uint(hr));
+ if (errors) {
+ *error = QString::fromUtf8(static_cast<const char *>(errors->GetBufferPointer()),
+ errors->GetBufferSize());
+ errors->Release();
+ }
+ return QByteArray();
+ }
+
+ QByteArray result;
+ result.resize(bytecode->GetBufferSize());
+ memcpy(result.data(), bytecode->GetBufferPointer(), result.size());
+ bytecode->Release();
+ return result;
+}
+
+bool QD3D11GraphicsPipeline::build()
+{
+ if (dsState)
+ release();
+
+ QRHI_RES_RHI(QRhiD3D11);
+
+ D3D11_RASTERIZER_DESC rastDesc;
+ memset(&rastDesc, 0, sizeof(rastDesc));
+ rastDesc.FillMode = D3D11_FILL_SOLID;
+ rastDesc.CullMode = toD3DCullMode(m_cullMode);
+ rastDesc.FrontCounterClockwise = m_frontFace == CCW;
+ rastDesc.ScissorEnable = m_flags.testFlag(UsesScissor);
+ rastDesc.MultisampleEnable = rhiD->effectiveSampleCount(m_sampleCount).Count > 1;
+ HRESULT hr = rhiD->dev->CreateRasterizerState(&rastDesc, &rastState);
+ if (FAILED(hr)) {
+ qWarning("Failed to create rasterizer state: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+
+ D3D11_DEPTH_STENCIL_DESC dsDesc;
+ memset(&dsDesc, 0, sizeof(dsDesc));
+ dsDesc.DepthEnable = m_depthTest;
+ dsDesc.DepthWriteMask = m_depthWrite ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO;
+ dsDesc.DepthFunc = toD3DCompareOp(m_depthOp);
+ dsDesc.StencilEnable = m_stencilTest;
+ if (m_stencilTest) {
+ dsDesc.StencilReadMask = m_stencilReadMask;
+ dsDesc.StencilWriteMask = m_stencilWriteMask;
+ dsDesc.FrontFace.StencilFailOp = toD3DStencilOp(m_stencilFront.failOp);
+ dsDesc.FrontFace.StencilDepthFailOp = toD3DStencilOp(m_stencilFront.depthFailOp);
+ dsDesc.FrontFace.StencilPassOp = toD3DStencilOp(m_stencilFront.passOp);
+ dsDesc.FrontFace.StencilFunc = toD3DCompareOp(m_stencilFront.compareOp);
+ dsDesc.BackFace.StencilFailOp = toD3DStencilOp(m_stencilBack.failOp);
+ dsDesc.BackFace.StencilDepthFailOp = toD3DStencilOp(m_stencilBack.depthFailOp);
+ dsDesc.BackFace.StencilPassOp = toD3DStencilOp(m_stencilBack.passOp);
+ dsDesc.BackFace.StencilFunc = toD3DCompareOp(m_stencilBack.compareOp);
+ }
+ hr = rhiD->dev->CreateDepthStencilState(&dsDesc, &dsState);
+ if (FAILED(hr)) {
+ qWarning("Failed to create depth-stencil state: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+
+ D3D11_BLEND_DESC blendDesc;
+ memset(&blendDesc, 0, sizeof(blendDesc));
+ blendDesc.IndependentBlendEnable = m_targetBlends.count() > 1;
+ for (int i = 0, ie = m_targetBlends.count(); i != ie; ++i) {
+ const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[i]);
+ D3D11_RENDER_TARGET_BLEND_DESC blend;
+ memset(&blend, 0, sizeof(blend));
+ blend.BlendEnable = b.enable;
+ blend.SrcBlend = toD3DBlendFactor(b.srcColor);
+ blend.DestBlend = toD3DBlendFactor(b.dstColor);
+ blend.BlendOp = toD3DBlendOp(b.opColor);
+ blend.SrcBlendAlpha = toD3DBlendFactor(b.srcAlpha);
+ blend.DestBlendAlpha = toD3DBlendFactor(b.dstAlpha);
+ blend.BlendOpAlpha = toD3DBlendOp(b.opAlpha);
+ blend.RenderTargetWriteMask = toD3DColorWriteMask(b.colorWrite);
+ blendDesc.RenderTarget[i] = blend;
+ }
+ if (m_targetBlends.isEmpty()) {
+ D3D11_RENDER_TARGET_BLEND_DESC blend;
+ memset(&blend, 0, sizeof(blend));
+ blend.RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
+ blendDesc.RenderTarget[0] = blend;
+ }
+ hr = rhiD->dev->CreateBlendState(&blendDesc, &blendState);
+ if (FAILED(hr)) {
+ qWarning("Failed to create blend state: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+
+ QByteArray vsByteCode;
+ for (const QRhiShaderStage &shaderStage : qAsConst(m_shaderStages)) {
+ QString error;
+ QByteArray bytecode = compileHlslShaderSource(shaderStage.shader(), shaderStage.shaderVariant(), &error);
+ if (bytecode.isEmpty()) {
+ qWarning("HLSL shader compilation failed: %s", qPrintable(error));
+ return false;
+ }
+ switch (shaderStage.type()) {
+ case QRhiShaderStage::Vertex:
+ hr = rhiD->dev->CreateVertexShader(bytecode.constData(), bytecode.size(), nullptr, &vs);
+ if (FAILED(hr)) {
+ qWarning("Failed to create vertex shader: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ vsByteCode = bytecode;
+ break;
+ case QRhiShaderStage::Fragment:
+ hr = rhiD->dev->CreatePixelShader(bytecode.constData(), bytecode.size(), nullptr, &fs);
+ if (FAILED(hr)) {
+ qWarning("Failed to create pixel shader: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ d3dTopology = toD3DTopology(m_topology);
+
+ if (!vsByteCode.isEmpty()) {
+ const QVector<QRhiVertexInputBinding> bindings = m_vertexInputLayout.bindings();
+ const QVector<QRhiVertexInputAttribute> attributes = m_vertexInputLayout.attributes();
+ QVarLengthArray<D3D11_INPUT_ELEMENT_DESC, 4> inputDescs;
+ for (const QRhiVertexInputAttribute &attribute : attributes) {
+ D3D11_INPUT_ELEMENT_DESC desc;
+ memset(&desc, 0, sizeof(desc));
+ // the output from SPIRV-Cross uses TEXCOORD<location> as the semantic
+ desc.SemanticName = "TEXCOORD";
+ desc.SemanticIndex = attribute.location();
+ desc.Format = toD3DAttributeFormat(attribute.format());
+ desc.InputSlot = attribute.binding();
+ desc.AlignedByteOffset = attribute.offset();
+ const QRhiVertexInputBinding &binding(bindings[attribute.binding()]);
+ if (binding.classification() == QRhiVertexInputBinding::PerInstance) {
+ desc.InputSlotClass = D3D11_INPUT_PER_INSTANCE_DATA;
+ desc.InstanceDataStepRate = binding.instanceStepRate();
+ } else {
+ desc.InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
+ }
+ inputDescs.append(desc);
+ }
+ hr = rhiD->dev->CreateInputLayout(inputDescs.constData(), inputDescs.count(), vsByteCode, vsByteCode.size(), &inputLayout);
+ if (FAILED(hr)) {
+ qWarning("Failed to create input layout: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ }
+
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+QD3D11ComputePipeline::QD3D11ComputePipeline(QRhiImplementation *rhi)
+ : QRhiComputePipeline(rhi)
+{
+}
+
+QD3D11ComputePipeline::~QD3D11ComputePipeline()
+{
+ release();
+}
+
+void QD3D11ComputePipeline::release()
+{
+ QRHI_RES_RHI(QRhiD3D11);
+
+ if (!cs)
+ return;
+
+ cs->Release();
+ cs = nullptr;
+
+ rhiD->unregisterResource(this);
+}
+
+bool QD3D11ComputePipeline::build()
+{
+ if (cs)
+ release();
+
+ QRHI_RES_RHI(QRhiD3D11);
+
+ QString error;
+ QByteArray bytecode = compileHlslShaderSource(m_shaderStage.shader(), m_shaderStage.shaderVariant(), &error);
+ if (bytecode.isEmpty()) {
+ qWarning("HLSL compute shader compilation failed: %s", qPrintable(error));
+ return false;
+ }
+
+ HRESULT hr = rhiD->dev->CreateComputeShader(bytecode.constData(), bytecode.size(), nullptr, &cs);
+ if (FAILED(hr)) {
+ qWarning("Failed to create compute shader: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+QD3D11CommandBuffer::QD3D11CommandBuffer(QRhiImplementation *rhi)
+ : QRhiCommandBuffer(rhi)
+{
+ resetState();
+}
+
+QD3D11CommandBuffer::~QD3D11CommandBuffer()
+{
+ release();
+}
+
+void QD3D11CommandBuffer::release()
+{
+ // nothing to do here
+}
+
+QD3D11SwapChain::QD3D11SwapChain(QRhiImplementation *rhi)
+ : QRhiSwapChain(rhi),
+ rt(rhi),
+ cb(rhi)
+{
+ for (int i = 0; i < BUFFER_COUNT; ++i) {
+ tex[i] = nullptr;
+ rtv[i] = nullptr;
+ msaaTex[i] = nullptr;
+ msaaRtv[i] = nullptr;
+ timestampActive[i] = false;
+ timestampDisjointQuery[i] = nullptr;
+ timestampQuery[2 * i] = nullptr;
+ timestampQuery[2 * i + 1] = nullptr;
+ }
+}
+
+QD3D11SwapChain::~QD3D11SwapChain()
+{
+ release();
+}
+
+void QD3D11SwapChain::releaseBuffers()
+{
+ for (int i = 0; i < BUFFER_COUNT; ++i) {
+ if (rtv[i]) {
+ rtv[i]->Release();
+ rtv[i] = nullptr;
+ }
+ if (tex[i]) {
+ tex[i]->Release();
+ tex[i] = nullptr;
+ }
+ if (msaaRtv[i]) {
+ msaaRtv[i]->Release();
+ msaaRtv[i] = nullptr;
+ }
+ if (msaaTex[i]) {
+ msaaTex[i]->Release();
+ msaaTex[i] = nullptr;
+ }
+ }
+}
+
+void QD3D11SwapChain::release()
+{
+ if (!swapChain)
+ return;
+
+ releaseBuffers();
+
+ for (int i = 0; i < BUFFER_COUNT; ++i) {
+ if (timestampDisjointQuery[i]) {
+ timestampDisjointQuery[i]->Release();
+ timestampDisjointQuery[i] = nullptr;
+ }
+ for (int j = 0; j < 2; ++j) {
+ const int idx = BUFFER_COUNT * i + j;
+ if (timestampQuery[idx]) {
+ timestampQuery[idx]->Release();
+ timestampQuery[idx] = nullptr;
+ }
+ }
+ }
+
+ swapChain->Release();
+ swapChain = nullptr;
+
+ QRHI_PROF;
+ QRHI_PROF_F(releaseSwapChain(this));
+
+ QRHI_RES_RHI(QRhiD3D11);
+ rhiD->unregisterResource(this);
+}
+
+QRhiCommandBuffer *QD3D11SwapChain::currentFrameCommandBuffer()
+{
+ return &cb;
+}
+
+QRhiRenderTarget *QD3D11SwapChain::currentFrameRenderTarget()
+{
+ return &rt;
+}
+
+QSize QD3D11SwapChain::surfacePixelSize()
+{
+ Q_ASSERT(m_window);
+ return m_window->size() * m_window->devicePixelRatio();
+}
+
+QRhiRenderPassDescriptor *QD3D11SwapChain::newCompatibleRenderPassDescriptor()
+{
+ return new QD3D11RenderPassDescriptor(m_rhi);
+}
+
+bool QD3D11SwapChain::newColorBuffer(const QSize &size, DXGI_FORMAT format, DXGI_SAMPLE_DESC sampleDesc,
+ ID3D11Texture2D **tex, ID3D11RenderTargetView **rtv) const
+{
+ D3D11_TEXTURE2D_DESC desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.Width = size.width();
+ desc.Height = size.height();
+ desc.MipLevels = 1;
+ desc.ArraySize = 1;
+ desc.Format = format;
+ desc.SampleDesc = sampleDesc;
+ desc.Usage = D3D11_USAGE_DEFAULT;
+ desc.BindFlags = D3D11_BIND_RENDER_TARGET;
+
+ QRHI_RES_RHI(QRhiD3D11);
+ HRESULT hr = rhiD->dev->CreateTexture2D(&desc, nullptr, tex);
+ if (FAILED(hr)) {
+ qWarning("Failed to create color buffer texture: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+
+ D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
+ memset(&rtvDesc, 0, sizeof(rtvDesc));
+ rtvDesc.Format = format;
+ rtvDesc.ViewDimension = sampleDesc.Count > 1 ? D3D11_RTV_DIMENSION_TEXTURE2DMS : D3D11_RTV_DIMENSION_TEXTURE2D;
+ hr = rhiD->dev->CreateRenderTargetView(*tex, &rtvDesc, rtv);
+ if (FAILED(hr)) {
+ qWarning("Failed to create color buffer rtv: %s", qPrintable(comErrorMessage(hr)));
+ (*tex)->Release();
+ *tex = nullptr;
+ return false;
+ }
+
+ return true;
+}
+
+bool QD3D11SwapChain::buildOrResize()
+{
+ // Can be called multiple times due to window resizes - that is not the
+ // same as a simple release+build (as with other resources). Just need to
+ // resize the buffers then.
+
+ const bool needsRegistration = !window || window != m_window;
+
+ // except if the window actually changes
+ if (window && window != m_window)
+ release();
+
+ window = m_window;
+ m_currentPixelSize = surfacePixelSize();
+ pixelSize = m_currentPixelSize;
+
+ if (pixelSize.isEmpty())
+ return false;
+
+ colorFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
+ const DXGI_FORMAT srgbAdjustedFormat = m_flags.testFlag(sRGB) ?
+ DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM;
+
+ const UINT swapChainFlags = 0;
+
+ QRHI_RES_RHI(QRhiD3D11);
+ if (!swapChain) {
+ HWND hwnd = reinterpret_cast<HWND>(window->winId());
+ sampleDesc = rhiD->effectiveSampleCount(m_sampleCount);
+
+ // We use FLIP_DISCARD which implies a buffer count of 2 (as opposed to the
+ // old DISCARD with back buffer count == 1). This makes no difference for
+ // the rest of the stuff except that automatic MSAA is unsupported and
+ // needs to be implemented via a custom multisample render target and an
+ // explicit resolve.
+
+ HRESULT hr;
+ if (rhiD->hasDxgi2) {
+ DXGI_SWAP_CHAIN_DESC1 desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.Width = pixelSize.width();
+ desc.Height = pixelSize.height();
+ desc.Format = colorFormat;
+ desc.SampleDesc.Count = 1;
+ desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ desc.BufferCount = BUFFER_COUNT;
+ desc.Scaling = DXGI_SCALING_STRETCH;
+ desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
+ if (m_flags.testFlag(SurfaceHasPreMulAlpha))
+ desc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
+ else if (m_flags.testFlag(SurfaceHasNonPreMulAlpha))
+ desc.AlphaMode = DXGI_ALPHA_MODE_STRAIGHT;
+ desc.Flags = swapChainFlags;
+
+ IDXGISwapChain1 *sc1;
+ hr = static_cast<IDXGIFactory2 *>(rhiD->dxgiFactory)->CreateSwapChainForHwnd(rhiD->dev, hwnd, &desc,
+ nullptr, nullptr, &sc1);
+ if (SUCCEEDED(hr))
+ swapChain = sc1;
+ } else {
+ // Windows 7
+ DXGI_SWAP_CHAIN_DESC desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.BufferDesc.Width = pixelSize.width();
+ desc.BufferDesc.Height = pixelSize.height();
+ desc.BufferDesc.RefreshRate.Numerator = 60;
+ desc.BufferDesc.RefreshRate.Denominator = 1;
+ desc.BufferDesc.Format = colorFormat;
+ desc.SampleDesc.Count = 1;
+ desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ desc.BufferCount = BUFFER_COUNT;
+ desc.OutputWindow = hwnd;
+ desc.Windowed = true;
+ desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
+ desc.Flags = swapChainFlags;
+
+ hr = rhiD->dxgiFactory->CreateSwapChain(rhiD->dev, &desc, &swapChain);
+ }
+ if (FAILED(hr)) {
+ qWarning("Failed to create D3D11 swapchain: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ } else {
+ releaseBuffers();
+ HRESULT hr = swapChain->ResizeBuffers(2, pixelSize.width(), pixelSize.height(), colorFormat, swapChainFlags);
+ if (FAILED(hr)) {
+ qWarning("Failed to resize D3D11 swapchain: %s", qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ }
+
+ for (int i = 0; i < BUFFER_COUNT; ++i) {
+ HRESULT hr = swapChain->GetBuffer(0, IID_ID3D11Texture2D, reinterpret_cast<void **>(&tex[i]));
+ if (FAILED(hr)) {
+ qWarning("Failed to query swapchain buffer %d: %s", i, qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
+ memset(&rtvDesc, 0, sizeof(rtvDesc));
+ rtvDesc.Format = srgbAdjustedFormat;
+ rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
+ hr = rhiD->dev->CreateRenderTargetView(tex[i], &rtvDesc, &rtv[i]);
+ if (FAILED(hr)) {
+ qWarning("Failed to create rtv for swapchain buffer %d: %s", i, qPrintable(comErrorMessage(hr)));
+ return false;
+ }
+ if (sampleDesc.Count > 1) {
+ if (!newColorBuffer(pixelSize, srgbAdjustedFormat, sampleDesc, &msaaTex[i], &msaaRtv[i]))
+ return false;
+ }
+ }
+
+ if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) {
+ qWarning("Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
+ m_depthStencil->sampleCount(), m_sampleCount);
+ }
+ if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) {
+ qWarning("Depth-stencil buffer's size (%dx%d) does not match the surface size (%dx%d). Expect problems.",
+ m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(),
+ pixelSize.width(), pixelSize.height());
+ }
+
+ currentFrameSlot = 0;
+ frameCount = 0;
+ ds = m_depthStencil ? QRHI_RES(QD3D11RenderBuffer, m_depthStencil) : nullptr;
+ swapInterval = m_flags.testFlag(QRhiSwapChain::NoVSync) ? 0 : 1;
+
+ QD3D11ReferenceRenderTarget *rtD = QRHI_RES(QD3D11ReferenceRenderTarget, &rt);
+ rtD->d.rp = QRHI_RES(QD3D11RenderPassDescriptor, m_renderPassDesc);
+ rtD->d.pixelSize = pixelSize;
+ rtD->d.dpr = window->devicePixelRatio();
+ rtD->d.sampleCount = sampleDesc.Count;
+ rtD->d.colorAttCount = 1;
+ rtD->d.dsAttCount = m_depthStencil ? 1 : 0;
+
+ QRHI_PROF;
+ QRHI_PROF_F(resizeSwapChain(this, BUFFER_COUNT, sampleDesc.Count > 1 ? BUFFER_COUNT : 0, sampleDesc.Count));
+ if (rhiP) {
+ D3D11_QUERY_DESC queryDesc;
+ memset(&queryDesc, 0, sizeof(queryDesc));
+ for (int i = 0; i < BUFFER_COUNT; ++i) {
+ if (!timestampDisjointQuery[i]) {
+ queryDesc.Query = D3D11_QUERY_TIMESTAMP_DISJOINT;
+ HRESULT hr = rhiD->dev->CreateQuery(&queryDesc, &timestampDisjointQuery[i]);
+ if (FAILED(hr)) {
+ qWarning("Failed to create timestamp disjoint query: %s", qPrintable(comErrorMessage(hr)));
+ break;
+ }
+ }
+ queryDesc.Query = D3D11_QUERY_TIMESTAMP;
+ for (int j = 0; j < 2; ++j) {
+ const int idx = BUFFER_COUNT * i + j; // one pair per buffer (frame)
+ if (!timestampQuery[idx]) {
+ HRESULT hr = rhiD->dev->CreateQuery(&queryDesc, &timestampQuery[idx]);
+ if (FAILED(hr)) {
+ qWarning("Failed to create timestamp query: %s", qPrintable(comErrorMessage(hr)));
+ break;
+ }
+ }
+ }
+ }
+ // timestamp queries are optional so we can go on even if they failed
+ }
+
+ if (needsRegistration)
+ rhiD->registerResource(this);
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/rhi/qrhid3d11_p.h b/src/gui/rhi/qrhid3d11_p.h
new file mode 100644
index 0000000000..3e2e492d9c
--- /dev/null
+++ b/src/gui/rhi/qrhid3d11_p.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHID3D11_H
+#define QRHID3D11_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qrhi_p.h>
+
+// no d3d includes here, to prevent precompiled header mess (due to this being
+// a public header)
+
+QT_BEGIN_NAMESPACE
+
+struct Q_GUI_EXPORT QRhiD3D11InitParams : public QRhiInitParams
+{
+ bool enableDebugLayer = false;
+};
+
+struct Q_GUI_EXPORT QRhiD3D11NativeHandles : public QRhiNativeHandles
+{
+ void *dev = nullptr;
+ void *context = nullptr;
+};
+
+struct Q_GUI_EXPORT QRhiD3D11TextureNativeHandles : public QRhiNativeHandles
+{
+ void *texture = nullptr; // ID3D11Texture2D*
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h
new file mode 100644
index 0000000000..688f79b3b7
--- /dev/null
+++ b/src/gui/rhi/qrhid3d11_p_p.h
@@ -0,0 +1,690 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHID3D11_P_H
+#define QRHID3D11_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qrhid3d11_p.h"
+#include "qrhi_p_p.h"
+#include "qshaderdescription_p.h"
+#include <QWindow>
+
+#include <d3d11_1.h>
+#include <dxgi1_3.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QD3D11Buffer : public QRhiBuffer
+{
+ QD3D11Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size);
+ ~QD3D11Buffer();
+ void release() override;
+ bool build() override;
+
+ ID3D11UnorderedAccessView *unorderedAccessView();
+
+ ID3D11Buffer *buffer = nullptr;
+ QByteArray dynBuf;
+ bool hasPendingDynamicUpdates = false;
+ ID3D11UnorderedAccessView *uav = nullptr;
+ uint generation = 0;
+ friend class QRhiD3D11;
+};
+
+struct QD3D11RenderBuffer : public QRhiRenderBuffer
+{
+ QD3D11RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags);
+ ~QD3D11RenderBuffer();
+ void release() override;
+ bool build() override;
+ QRhiTexture::Format backingFormat() const override;
+
+ ID3D11Texture2D *tex = nullptr;
+ ID3D11DepthStencilView *dsv = nullptr;
+ ID3D11RenderTargetView *rtv = nullptr;
+ DXGI_FORMAT dxgiFormat;
+ DXGI_SAMPLE_DESC sampleDesc;
+ friend class QRhiD3D11;
+};
+
+struct QD3D11Texture : public QRhiTexture
+{
+ QD3D11Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+ int sampleCount, Flags flags);
+ ~QD3D11Texture();
+ void release() override;
+ bool build() override;
+ bool buildFrom(const QRhiNativeHandles *src) override;
+ const QRhiNativeHandles *nativeHandles() override;
+
+ bool prepareBuild(QSize *adjustedSize = nullptr);
+ bool finishBuild();
+ ID3D11UnorderedAccessView *unorderedAccessViewForLevel(int level);
+
+ ID3D11Texture2D *tex = nullptr;
+ bool owns = true;
+ ID3D11ShaderResourceView *srv = nullptr;
+ DXGI_FORMAT dxgiFormat;
+ uint mipLevelCount = 0;
+ DXGI_SAMPLE_DESC sampleDesc;
+ QRhiD3D11TextureNativeHandles nativeHandlesStruct;
+ ID3D11UnorderedAccessView *perLevelViews[QRhi::MAX_LEVELS];
+ uint generation = 0;
+ friend class QRhiD3D11;
+};
+
+struct QD3D11Sampler : public QRhiSampler
+{
+ QD3D11Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
+ AddressMode u, AddressMode v);
+ ~QD3D11Sampler();
+ void release() override;
+ bool build() override;
+
+ ID3D11SamplerState *samplerState = nullptr;
+ uint generation = 0;
+ friend class QRhiD3D11;
+};
+
+struct QD3D11RenderPassDescriptor : public QRhiRenderPassDescriptor
+{
+ QD3D11RenderPassDescriptor(QRhiImplementation *rhi);
+ ~QD3D11RenderPassDescriptor();
+ void release() override;
+};
+
+struct QD3D11RenderTargetData
+{
+ QD3D11RenderTargetData(QRhiImplementation *)
+ {
+ for (int i = 0; i < MAX_COLOR_ATTACHMENTS; ++i)
+ rtv[i] = nullptr;
+ }
+
+ QD3D11RenderPassDescriptor *rp = nullptr;
+ QSize pixelSize;
+ float dpr = 1;
+ int sampleCount = 1;
+ int colorAttCount = 0;
+ int dsAttCount = 0;
+
+ static const int MAX_COLOR_ATTACHMENTS = 8;
+ ID3D11RenderTargetView *rtv[MAX_COLOR_ATTACHMENTS];
+ ID3D11DepthStencilView *dsv = nullptr;
+};
+
+struct QD3D11ReferenceRenderTarget : public QRhiRenderTarget
+{
+ QD3D11ReferenceRenderTarget(QRhiImplementation *rhi);
+ ~QD3D11ReferenceRenderTarget();
+ void release() override;
+
+ QSize pixelSize() const override;
+ float devicePixelRatio() const override;
+ int sampleCount() const override;
+
+ QD3D11RenderTargetData d;
+};
+
+struct QD3D11TextureRenderTarget : public QRhiTextureRenderTarget
+{
+ QD3D11TextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc, Flags flags);
+ ~QD3D11TextureRenderTarget();
+ void release() override;
+
+ QSize pixelSize() const override;
+ float devicePixelRatio() const override;
+ int sampleCount() const override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+ bool build() override;
+
+ QD3D11RenderTargetData d;
+ bool ownsRtv[QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS];
+ ID3D11RenderTargetView *rtv[QD3D11RenderTargetData::MAX_COLOR_ATTACHMENTS];
+ bool ownsDsv = false;
+ ID3D11DepthStencilView *dsv = nullptr;
+ friend class QRhiD3D11;
+};
+
+struct QD3D11ShaderResourceBindings : public QRhiShaderResourceBindings
+{
+ QD3D11ShaderResourceBindings(QRhiImplementation *rhi);
+ ~QD3D11ShaderResourceBindings();
+ void release() override;
+ bool build() override;
+
+ QVector<QRhiShaderResourceBinding> sortedBindings;
+ uint generation = 0;
+
+ // Keep track of the generation number of each referenced QRhi* to be able
+ // to detect that the batched bindings are out of date.
+ struct BoundUniformBufferData {
+ quint64 id;
+ uint generation;
+ };
+ struct BoundSampledTextureData {
+ quint64 texId;
+ uint texGeneration;
+ quint64 samplerId;
+ uint samplerGeneration;
+ };
+ struct BoundStorageImageData {
+ quint64 id;
+ uint generation;
+ };
+ struct BoundStorageBufferData {
+ quint64 id;
+ uint generation;
+ };
+ struct BoundResourceData {
+ union {
+ BoundUniformBufferData ubuf;
+ BoundSampledTextureData stex;
+ BoundStorageImageData simage;
+ BoundStorageBufferData sbuf;
+ };
+ };
+ QVector<BoundResourceData> boundResourceData;
+
+ QRhiBatchedBindings<ID3D11Buffer *> vsubufs;
+ QRhiBatchedBindings<UINT> vsubufoffsets;
+ QRhiBatchedBindings<UINT> vsubufsizes;
+
+ QRhiBatchedBindings<ID3D11Buffer *> fsubufs;
+ QRhiBatchedBindings<UINT> fsubufoffsets;
+ QRhiBatchedBindings<UINT> fsubufsizes;
+
+ QRhiBatchedBindings<ID3D11Buffer *> csubufs;
+ QRhiBatchedBindings<UINT> csubufoffsets;
+ QRhiBatchedBindings<UINT> csubufsizes;
+
+ QRhiBatchedBindings<ID3D11SamplerState *> vssamplers;
+ QRhiBatchedBindings<ID3D11ShaderResourceView *> vsshaderresources;
+
+ QRhiBatchedBindings<ID3D11SamplerState *> fssamplers;
+ QRhiBatchedBindings<ID3D11ShaderResourceView *> fsshaderresources;
+
+ QRhiBatchedBindings<ID3D11SamplerState *> cssamplers;
+ QRhiBatchedBindings<ID3D11ShaderResourceView *> csshaderresources;
+
+ QRhiBatchedBindings<ID3D11UnorderedAccessView *> csUAVs;
+
+ friend class QRhiD3D11;
+};
+
+Q_DECLARE_TYPEINFO(QD3D11ShaderResourceBindings::BoundResourceData, Q_MOVABLE_TYPE);
+
+struct QD3D11GraphicsPipeline : public QRhiGraphicsPipeline
+{
+ QD3D11GraphicsPipeline(QRhiImplementation *rhi);
+ ~QD3D11GraphicsPipeline();
+ void release() override;
+ bool build() override;
+
+ ID3D11DepthStencilState *dsState = nullptr;
+ ID3D11BlendState *blendState = nullptr;
+ ID3D11VertexShader *vs = nullptr;
+ ID3D11PixelShader *fs = nullptr;
+ ID3D11InputLayout *inputLayout = nullptr;
+ D3D11_PRIMITIVE_TOPOLOGY d3dTopology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
+ ID3D11RasterizerState *rastState = nullptr;
+ uint generation = 0;
+ friend class QRhiD3D11;
+};
+
+struct QD3D11ComputePipeline : public QRhiComputePipeline
+{
+ QD3D11ComputePipeline(QRhiImplementation *rhi);
+ ~QD3D11ComputePipeline();
+ void release() override;
+ bool build() override;
+
+ ID3D11ComputeShader *cs = nullptr;
+ uint generation = 0;
+ friend class QRhiD3D11;
+};
+
+struct QD3D11SwapChain;
+
+struct QD3D11CommandBuffer : public QRhiCommandBuffer
+{
+ QD3D11CommandBuffer(QRhiImplementation *rhi);
+ ~QD3D11CommandBuffer();
+ void release() override;
+
+ struct Command {
+ enum Cmd {
+ ResetShaderResources,
+ SetRenderTarget,
+ Clear,
+ Viewport,
+ Scissor,
+ BindVertexBuffers,
+ BindIndexBuffer,
+ BindGraphicsPipeline,
+ BindShaderResources,
+ StencilRef,
+ BlendConstants,
+ Draw,
+ DrawIndexed,
+ UpdateSubRes,
+ CopySubRes,
+ ResolveSubRes,
+ GenMip,
+ DebugMarkBegin,
+ DebugMarkEnd,
+ DebugMarkMsg,
+ BindComputePipeline,
+ Dispatch
+ };
+ enum ClearFlag { Color = 1, Depth = 2, Stencil = 4 };
+ Cmd cmd;
+
+ static const int MAX_UBUF_BINDINGS = 32; // should be D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT but 128 is a waste of space for our purposes
+
+ // QRhi*/QD3D11* references should be kept at minimum (so no
+ // QRhiTexture/Buffer/etc. pointers).
+ union {
+ struct {
+ QRhiRenderTarget *rt;
+ } setRenderTarget;
+ struct {
+ QRhiRenderTarget *rt;
+ int mask;
+ float c[4];
+ float d;
+ quint32 s;
+ } clear;
+ struct {
+ float x, y, w, h;
+ float d0, d1;
+ } viewport;
+ struct {
+ int x, y, w, h;
+ } scissor;
+ struct {
+ int startSlot;
+ int slotCount;
+ ID3D11Buffer *buffers[D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT];
+ UINT offsets[D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT];
+ UINT strides[D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT];
+ } bindVertexBuffers;
+ struct {
+ ID3D11Buffer *buffer;
+ quint32 offset;
+ DXGI_FORMAT format;
+ } bindIndexBuffer;
+ struct {
+ QD3D11GraphicsPipeline *ps;
+ } bindGraphicsPipeline;
+ struct {
+ QD3D11ShaderResourceBindings *srb;
+ bool offsetOnlyChange;
+ int dynamicOffsetCount;
+ uint dynamicOffsetPairs[MAX_UBUF_BINDINGS * 2]; // binding, offsetInConstants
+ } bindShaderResources;
+ struct {
+ QD3D11GraphicsPipeline *ps;
+ quint32 ref;
+ } stencilRef;
+ struct {
+ QD3D11GraphicsPipeline *ps;
+ float c[4];
+ } blendConstants;
+ struct {
+ QD3D11GraphicsPipeline *ps;
+ quint32 vertexCount;
+ quint32 instanceCount;
+ quint32 firstVertex;
+ quint32 firstInstance;
+ } draw;
+ struct {
+ QD3D11GraphicsPipeline *ps;
+ quint32 indexCount;
+ quint32 instanceCount;
+ quint32 firstIndex;
+ qint32 vertexOffset;
+ quint32 firstInstance;
+ } drawIndexed;
+ struct {
+ ID3D11Resource *dst;
+ UINT dstSubRes;
+ bool hasDstBox;
+ D3D11_BOX dstBox;
+ const void *src; // must come from retain*()
+ UINT srcRowPitch;
+ } updateSubRes;
+ struct {
+ ID3D11Resource *dst;
+ UINT dstSubRes;
+ UINT dstX;
+ UINT dstY;
+ ID3D11Resource *src;
+ UINT srcSubRes;
+ bool hasSrcBox;
+ D3D11_BOX srcBox;
+ } copySubRes;
+ struct {
+ ID3D11Resource *dst;
+ UINT dstSubRes;
+ ID3D11Resource *src;
+ UINT srcSubRes;
+ DXGI_FORMAT format;
+ } resolveSubRes;
+ struct {
+ ID3D11ShaderResourceView *srv;
+ } genMip;
+ struct {
+ char s[64];
+ } debugMark;
+ struct {
+ QD3D11ComputePipeline *ps;
+ } bindComputePipeline;
+ struct {
+ UINT x;
+ UINT y;
+ UINT z;
+ } dispatch;
+ } args;
+ };
+
+ enum PassType {
+ NoPass,
+ RenderPass,
+ ComputePass
+ };
+
+ QVector<Command> commands;
+ PassType recordingPass;
+ QRhiRenderTarget *currentTarget;
+ QRhiGraphicsPipeline *currentGraphicsPipeline;
+ QRhiComputePipeline *currentComputePipeline;
+ uint currentPipelineGeneration;
+ QRhiShaderResourceBindings *currentGraphicsSrb;
+ QRhiShaderResourceBindings *currentComputeSrb;
+ uint currentSrbGeneration;
+ ID3D11Buffer *currentIndexBuffer;
+ quint32 currentIndexOffset;
+ DXGI_FORMAT currentIndexFormat;
+ ID3D11Buffer *currentVertexBuffers[D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT];
+ quint32 currentVertexOffsets[D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT];
+
+ QVector<QByteArray> dataRetainPool;
+ QVector<QImage> imageRetainPool;
+
+ // relies heavily on implicit sharing (no copies of the actual data will be made)
+ const uchar *retainData(const QByteArray &data) {
+ dataRetainPool.append(data);
+ return reinterpret_cast<const uchar *>(dataRetainPool.constLast().constData());
+ }
+ const uchar *retainImage(const QImage &image) {
+ imageRetainPool.append(image);
+ return imageRetainPool.constLast().constBits();
+ }
+ void resetCommands() {
+ commands.clear();
+ dataRetainPool.clear();
+ imageRetainPool.clear();
+ }
+ void resetState() {
+ resetCommands();
+ recordingPass = NoPass;
+ currentTarget = nullptr;
+ resetCachedState();
+ }
+ void resetCachedState() {
+ currentGraphicsPipeline = nullptr;
+ currentComputePipeline = nullptr;
+ currentPipelineGeneration = 0;
+ currentGraphicsSrb = nullptr;
+ currentComputeSrb = nullptr;
+ currentSrbGeneration = 0;
+ currentIndexBuffer = nullptr;
+ currentIndexOffset = 0;
+ currentIndexFormat = DXGI_FORMAT_R16_UINT;
+ memset(currentVertexBuffers, 0, sizeof(currentVertexBuffers));
+ memset(currentVertexOffsets, 0, sizeof(currentVertexOffsets));
+ }
+};
+
+Q_DECLARE_TYPEINFO(QD3D11CommandBuffer::Command, Q_MOVABLE_TYPE);
+
+struct QD3D11SwapChain : public QRhiSwapChain
+{
+ QD3D11SwapChain(QRhiImplementation *rhi);
+ ~QD3D11SwapChain();
+ void release() override;
+
+ QRhiCommandBuffer *currentFrameCommandBuffer() override;
+ QRhiRenderTarget *currentFrameRenderTarget() override;
+
+ QSize surfacePixelSize() override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+ bool buildOrResize() override;
+
+ void releaseBuffers();
+ bool newColorBuffer(const QSize &size, DXGI_FORMAT format, DXGI_SAMPLE_DESC sampleDesc,
+ ID3D11Texture2D **tex, ID3D11RenderTargetView **rtv) const;
+
+ QWindow *window = nullptr;
+ QSize pixelSize;
+ QD3D11ReferenceRenderTarget rt;
+ QD3D11CommandBuffer cb;
+ DXGI_FORMAT colorFormat;
+ IDXGISwapChain *swapChain = nullptr;
+ static const int BUFFER_COUNT = 2;
+ ID3D11Texture2D *tex[BUFFER_COUNT];
+ ID3D11RenderTargetView *rtv[BUFFER_COUNT];
+ ID3D11Texture2D *msaaTex[BUFFER_COUNT];
+ ID3D11RenderTargetView *msaaRtv[BUFFER_COUNT];
+ DXGI_SAMPLE_DESC sampleDesc;
+ int currentFrameSlot = 0;
+ int frameCount = 0;
+ QD3D11RenderBuffer *ds = nullptr;
+ bool timestampActive[BUFFER_COUNT];
+ ID3D11Query *timestampDisjointQuery[BUFFER_COUNT];
+ ID3D11Query *timestampQuery[BUFFER_COUNT * 2];
+ UINT swapInterval = 1;
+};
+
+class QRhiD3D11 : public QRhiImplementation
+{
+public:
+ QRhiD3D11(QRhiD3D11InitParams *params, QRhiD3D11NativeHandles *importDevice = nullptr);
+
+ bool create(QRhi::Flags flags) override;
+ void destroy() override;
+
+ QRhiGraphicsPipeline *createGraphicsPipeline() override;
+ QRhiComputePipeline *createComputePipeline() override;
+ QRhiShaderResourceBindings *createShaderResourceBindings() override;
+ QRhiBuffer *createBuffer(QRhiBuffer::Type type,
+ QRhiBuffer::UsageFlags usage,
+ int size) override;
+ QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiRenderBuffer::Flags flags) override;
+ QRhiTexture *createTexture(QRhiTexture::Format format,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiTexture::Flags flags) override;
+ QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler:: AddressMode u, QRhiSampler::AddressMode v) override;
+
+ QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
+ QRhiTextureRenderTarget::Flags flags) override;
+
+ QRhiSwapChain *createSwapChain() override;
+ QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
+ QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
+ QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override;
+ QRhi::FrameOpResult endOffscreenFrame() override;
+ QRhi::FrameOpResult finish() override;
+
+ void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+
+ void beginPass(QRhiCommandBuffer *cb,
+ QRhiRenderTarget *rt,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ QRhiResourceUpdateBatch *resourceUpdates) override;
+ void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+
+ void setGraphicsPipeline(QRhiCommandBuffer *cb,
+ QRhiGraphicsPipeline *ps) override;
+
+ void setShaderResources(QRhiCommandBuffer *cb,
+ QRhiShaderResourceBindings *srb,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) override;
+
+ void setVertexInput(QRhiCommandBuffer *cb,
+ int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
+ QRhiBuffer *indexBuf, quint32 indexOffset,
+ QRhiCommandBuffer::IndexFormat indexFormat) override;
+
+ void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) override;
+ void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override;
+ void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
+ void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
+
+ void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
+ quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
+
+ void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
+ quint32 instanceCount, quint32 firstIndex,
+ qint32 vertexOffset, quint32 firstInstance) override;
+
+ void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) override;
+ void debugMarkEnd(QRhiCommandBuffer *cb) override;
+ void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override;
+
+ void beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+ void endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+ void setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) override;
+ void dispatch(QRhiCommandBuffer *cb, int x, int y, int z) override;
+
+ const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) override;
+ void beginExternal(QRhiCommandBuffer *cb) override;
+ void endExternal(QRhiCommandBuffer *cb) override;
+
+ QVector<int> supportedSampleCounts() const override;
+ int ubufAlignment() const override;
+ bool isYUpInFramebuffer() const override;
+ bool isYUpInNDC() const override;
+ bool isClipDepthZeroToOne() const override;
+ QMatrix4x4 clipSpaceCorrMatrix() const override;
+ bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const override;
+ bool isFeatureSupported(QRhi::Feature feature) const override;
+ int resourceLimit(QRhi::ResourceLimit limit) const override;
+ const QRhiNativeHandles *nativeHandles() override;
+ void sendVMemStatsToProfiler() override;
+ void makeThreadLocalNativeContextCurrent() override;
+
+ void flushCommandBuffer();
+ void enqueueSubresUpload(QD3D11Texture *texD, QD3D11CommandBuffer *cbD,
+ int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc);
+ void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates);
+ void updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD);
+ void executeBufferHostWritesForCurrentFrame(QD3D11Buffer *bufD);
+ void bindShaderResources(QD3D11ShaderResourceBindings *srbD,
+ const uint *dynOfsPairs, int dynOfsPairCount,
+ bool offsetOnlyChange);
+ void resetShaderResources();
+ void executeCommandBuffer(QD3D11CommandBuffer *cbD, QD3D11SwapChain *timestampSwapChain = nullptr);
+ DXGI_SAMPLE_DESC effectiveSampleCount(int sampleCount) const;
+ void finishActiveReadbacks();
+ void reportLiveObjects(ID3D11Device *device);
+
+ bool debugLayer = false;
+ bool importedDevice = false;
+ ID3D11Device *dev = nullptr;
+ ID3D11DeviceContext1 *context = nullptr;
+ D3D_FEATURE_LEVEL featureLevel;
+ ID3DUserDefinedAnnotation *annotations = nullptr;
+ IDXGIFactory1 *dxgiFactory = nullptr;
+ bool hasDxgi2 = false;
+ QRhiD3D11NativeHandles nativeHandlesStruct;
+
+ struct {
+ int vsHighestActiveVertexBufferBinding = -1;
+ bool vsHasIndexBufferBound = false;
+ int vsHighestActiveSrvBinding = -1;
+ int fsHighestActiveSrvBinding = -1;
+ int csHighestActiveSrvBinding = -1;
+ int csHighestActiveUavBinding = -1;
+ QD3D11SwapChain *currentSwapChain = nullptr;
+ } contextState;
+
+ struct OffscreenFrame {
+ OffscreenFrame(QRhiImplementation *rhi) : cbWrapper(rhi) { }
+ bool active = false;
+ QD3D11CommandBuffer cbWrapper;
+ } ofr;
+
+ struct ActiveReadback {
+ QRhiReadbackDescription desc;
+ QRhiReadbackResult *result;
+ ID3D11Texture2D *stagingTex;
+ quint32 bufSize;
+ quint32 bpl;
+ QSize pixelSize;
+ QRhiTexture::Format format;
+ };
+ QVector<ActiveReadback> activeReadbacks;
+};
+
+Q_DECLARE_TYPEINFO(QRhiD3D11::ActiveReadback, Q_MOVABLE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp
new file mode 100644
index 0000000000..7c40a36701
--- /dev/null
+++ b/src/gui/rhi/qrhigles2.cpp
@@ -0,0 +1,3036 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qrhigles2_p_p.h"
+#include <QWindow>
+#include <QOffscreenSurface>
+#include <QOpenGLContext>
+#include <QtGui/private/qopenglextensions_p.h>
+#include <qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+/*
+ OpenGL backend. Binding vertex attribute locations and decomposing uniform
+ buffers into uniforms are handled transparently to the application via the
+ reflection data (QShaderDescription). Real uniform buffers are never used,
+ regardless of the GLSL version. Textures and buffers feature no special logic,
+ it's all just glTexSubImage2D and glBufferSubData (with "dynamic" buffers set
+ to GL_DYNAMIC_DRAW). The swapchain and the associated renderbuffer for
+ depth-stencil will be dummies since we have no control over the underlying
+ buffers here. While we try to keep this backend clean GLES 2.0, some GL(ES)
+ 3.0 features like multisample renderbuffers and blits are used when available.
+*/
+
+/*!
+ \class QRhiGles2InitParams
+ \inmodule QtRhi
+ \brief OpenGL specific initialization parameters.
+
+ An OpenGL-based QRhi needs an already created QOffscreenSurface at minimum.
+ Additionally, while optional, it is recommended that the QWindow the first
+ QRhiSwapChain will target is passed in as well.
+
+ \badcode
+ QOffscreenSurface *fallbackSurface = QRhiGles2InitParams::newFallbackSurface();
+ QRhiGles2InitParams params;
+ params.fallbackSurface = fallbackSurface;
+ params.window = window;
+ rhi = QRhi::create(QRhi::OpenGLES2, &params);
+ \endcode
+
+ By default QRhi creates a QOpenGLContext on its own. This approach works
+ well in most cases, included threaded scenarios, where there is a dedicated
+ QRhi for each rendering thread. As there will be a QOpenGLContext for each
+ QRhi, the OpenGL context requirements (a context can only be current on one
+ thread) are satisfied. The implicitly created context is destroyed
+ automatically together with the QRhi.
+
+ The QSurfaceFormat for the context is specified in \l format. The
+ constructor sets this to QSurfaceFormat::defaultFormat() so applications
+ that use QSurfaceFormat::setDefaultFormat() do not need to set the format
+ again.
+
+ \note The depth and stencil buffer sizes are set automatically to 24 and 8
+ when no size was explicitly set for these buffers in \l format. As there
+ are possible adjustments to \l format, applications can use
+ adjustedFormat() to query the effective format that is passed to
+ QOpenGLContext::setFormat() internally.
+
+ A QOffscreenSurface has to be specified in \l fallbackSurface. In order to
+ prevent mistakes in threaded situations, this is never created
+ automatically by the QRhi since, like QWindow, QOffscreenSurface can only
+ be created on the gui/main thread.
+
+ As a convenience, applications can use newFallbackSurface() which creates
+ and returns a QOffscreenSurface that is compatible with the QOpenGLContext
+ that is going to be created by the QRhi afterwards. Note that the ownership
+ of the returned QOffscreenSurface is transferred to the caller and the QRhi
+ will not destroy it.
+
+ \note QRhiSwapChain can only target QWindow instances that have their
+ surface type set to QSurface::OpenGLSurface.
+
+ \note \l window is optional. It is recommended to specify it whenever
+ possible, in order to avoid problems on multi-adapter and multi-screen
+ systems. When \l window is not set, the very first
+ QOpenGLContext::makeCurrent() happens with \l fallbackSurface which may be
+ an invisible window on some platforms (for example, Windows) and that may
+ trigger unexpected problems in some cases.
+
+ \section2 Working with existing OpenGL contexts
+
+ When interoperating with another graphics engine, it may be necessary to
+ get a QRhi instance that uses the same OpenGL context. This can be achieved
+ by passing a pointer to a QRhiGles2NativeHandles to QRhi::create(). The
+ \l{QRhiGles2NativeHandles::context}{context} must be set to a non-null
+ value.
+
+ An alternative approach is to create a QOpenGLContext that
+ \l{QOpenGLContext::setShareContext()}{shares resources} with the other
+ engine's context and passing in that context via QRhiGles2NativeHandles.
+
+ The QRhi does not take ownership of the QOpenGLContext passed in via
+ QRhiGles2NativeHandles.
+ */
+
+/*!
+ \class QRhiGles2NativeHandles
+ \inmodule QtRhi
+ \brief Holds the OpenGL context used by the QRhi.
+ */
+
+/*!
+ \class QRhiGles2TextureNativeHandles
+ \inmodule QtRhi
+ \brief Holds the OpenGL texture object that is backing a QRhiTexture instance.
+ */
+
+#ifndef GL_BGRA
+#define GL_BGRA 0x80E1
+#endif
+
+#ifndef GL_R8
+#define GL_R8 0x8229
+#endif
+
+#ifndef GL_R16
+#define GL_R16 0x822A
+#endif
+
+#ifndef GL_RED
+#define GL_RED 0x1903
+#endif
+
+#ifndef GL_RGBA8
+#define GL_RGBA8 0x8058
+#endif
+
+#ifndef GL_RGBA32F
+#define GL_RGBA32F 0x8814
+#endif
+
+#ifndef GL_RGBA16F
+#define GL_RGBA16F 0x881A
+#endif
+
+#ifndef GL_HALF_FLOAT
+#define GL_HALF_FLOAT 0x140B
+#endif
+
+#ifndef GL_DEPTH_COMPONENT16
+#define GL_DEPTH_COMPONENT16 0x81A5
+#endif
+
+#ifndef GL_DEPTH_COMPONENT32F
+#define GL_DEPTH_COMPONENT32F 0x8CAC
+#endif
+
+#ifndef GL_DEPTH24_STENCIL8
+#define GL_DEPTH24_STENCIL8 0x88F0
+#endif
+
+#ifndef GL_PRIMITIVE_RESTART_FIXED_INDEX
+#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69
+#endif
+
+#ifndef GL_FRAMEBUFFER_SRGB
+#define GL_FRAMEBUFFER_SRGB 0x8DB9
+#endif
+
+#ifndef GL_READ_FRAMEBUFFER
+#define GL_READ_FRAMEBUFFER 0x8CA8
+#endif
+
+#ifndef GL_DRAW_FRAMEBUFFER
+#define GL_DRAW_FRAMEBUFFER 0x8CA9
+#endif
+
+#ifndef GL_MAX_DRAW_BUFFERS
+#define GL_MAX_DRAW_BUFFERS 0x8824
+#endif
+
+#ifndef GL_TEXTURE_COMPARE_MODE
+#define GL_TEXTURE_COMPARE_MODE 0x884C
+#endif
+
+#ifndef GL_COMPARE_REF_TO_TEXTURE
+#define GL_COMPARE_REF_TO_TEXTURE 0x884E
+#endif
+
+#ifndef GL_TEXTURE_COMPARE_FUNC
+#define GL_TEXTURE_COMPARE_FUNC 0x884D
+#endif
+
+#ifndef GL_MAX_SAMPLES
+#define GL_MAX_SAMPLES 0x8D57
+#endif
+
+/*!
+ Constructs a new QRhiGles2InitParams.
+
+ \l format is set to QSurfaceFormat::defaultFormat().
+ */
+QRhiGles2InitParams::QRhiGles2InitParams()
+{
+ format = QSurfaceFormat::defaultFormat();
+}
+
+/*!
+ \return the QSurfaceFormat that will be set on the QOpenGLContext before
+ calling QOpenGLContext::create(). This format is based on \a format, but
+ may be adjusted. Applicable only when QRhi creates the context.
+ Applications are advised to set this format on their QWindow in order to
+ avoid potential BAD_MATCH failures.
+ */
+QSurfaceFormat QRhiGles2InitParams::adjustedFormat(const QSurfaceFormat &format)
+{
+ QSurfaceFormat fmt = format;
+
+ if (fmt.depthBufferSize() == -1)
+ fmt.setDepthBufferSize(24);
+ if (fmt.stencilBufferSize() == -1)
+ fmt.setStencilBufferSize(8);
+
+ return fmt;
+}
+
+/*!
+ \return a new QOffscreenSurface that can be used with a QRhi by passing it
+ via a QRhiGles2InitParams.
+
+ \a format is adjusted as appropriate in order to avoid having problems
+ afterwards due to an incompatible context and surface.
+
+ \note This function must only be called on the gui/main thread.
+
+ \note It is the application's responsibility to destroy the returned
+ QOffscreenSurface on the gui/main thread once the associated QRhi has been
+ destroyed. The QRhi will not destroy the QOffscreenSurface.
+ */
+QOffscreenSurface *QRhiGles2InitParams::newFallbackSurface(const QSurfaceFormat &format)
+{
+ QSurfaceFormat fmt = adjustedFormat(format);
+
+ // To resolve all fields in the format as much as possible, create a context.
+ // This may be heavy, but allows avoiding BAD_MATCH on some systems.
+ QOpenGLContext tempContext;
+ tempContext.setFormat(fmt);
+ if (tempContext.create())
+ fmt = tempContext.format();
+ else
+ qWarning("QRhiGles2: Failed to create temporary context");
+
+ QOffscreenSurface *s = new QOffscreenSurface;
+ s->setFormat(fmt);
+ s->create();
+
+ return s;
+}
+
+QRhiGles2::QRhiGles2(QRhiGles2InitParams *params, QRhiGles2NativeHandles *importDevice)
+ : ofr(this)
+{
+ requestedFormat = QRhiGles2InitParams::adjustedFormat(params->format);
+ fallbackSurface = params->fallbackSurface;
+ maybeWindow = params->window; // may be null
+
+ importedContext = importDevice != nullptr;
+ if (importedContext) {
+ ctx = importDevice->context;
+ if (!ctx) {
+ qWarning("No OpenGL context given, cannot import");
+ importedContext = false;
+ }
+ }
+}
+
+bool QRhiGles2::ensureContext(QSurface *surface) const
+{
+ bool nativeWindowGone = false;
+ if (surface && surface->surfaceClass() == QSurface::Window && !surface->surfaceHandle()) {
+ surface = fallbackSurface;
+ nativeWindowGone = true;
+ }
+
+ if (!surface)
+ surface = fallbackSurface;
+
+ if (needsMakeCurrent)
+ needsMakeCurrent = false;
+ else if (!nativeWindowGone && QOpenGLContext::currentContext() == ctx && (surface == fallbackSurface || ctx->surface() == surface))
+ return true;
+
+ if (!ctx->makeCurrent(surface)) {
+ qWarning("QRhiGles2: Failed to make context current. Expect bad things to happen.");
+ return false;
+ }
+
+ return true;
+}
+
+bool QRhiGles2::create(QRhi::Flags flags)
+{
+ Q_UNUSED(flags);
+ Q_ASSERT(fallbackSurface);
+
+ if (!importedContext) {
+ ctx = new QOpenGLContext;
+ ctx->setFormat(requestedFormat);
+ if (!ctx->create()) {
+ qWarning("QRhiGles2: Failed to create context");
+ delete ctx;
+ ctx = nullptr;
+ return false;
+ }
+ qDebug() << "Created OpenGL context" << ctx->format();
+ }
+
+ if (!ensureContext(maybeWindow ? maybeWindow : fallbackSurface)) // see 'window' discussion in QRhiGles2InitParams comments
+ return false;
+
+ f = static_cast<QOpenGLExtensions *>(ctx->extraFunctions());
+
+ const char *vendor = reinterpret_cast<const char *>(f->glGetString(GL_VENDOR));
+ const char *renderer = reinterpret_cast<const char *>(f->glGetString(GL_RENDERER));
+ const char *version = reinterpret_cast<const char *>(f->glGetString(GL_VERSION));
+ if (vendor && renderer && version)
+ qDebug("OpenGL VENDOR: %s RENDERER: %s VERSION: %s", vendor, renderer, version);
+
+ const QSurfaceFormat actualFormat = ctx->format();
+
+ caps.ctxMajor = actualFormat.majorVersion();
+ caps.ctxMinor = actualFormat.minorVersion();
+
+ GLint n = 0;
+ f->glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &n);
+ supportedCompressedFormats.resize(n);
+ if (n > 0)
+ f->glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, supportedCompressedFormats.data());
+
+ f->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &caps.maxTextureSize);
+
+ if (caps.ctxMajor >= 3 || actualFormat.renderableType() == QSurfaceFormat::OpenGL) {
+ f->glGetIntegerv(GL_MAX_DRAW_BUFFERS, &caps.maxDrawBuffers);
+ f->glGetIntegerv(GL_MAX_SAMPLES, &caps.maxSamples);
+ caps.maxSamples = qMax(1, caps.maxSamples);
+ } else {
+ caps.maxDrawBuffers = 1;
+ caps.maxSamples = 1;
+ }
+
+ caps.msaaRenderBuffer = f->hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample)
+ && f->hasOpenGLExtension(QOpenGLExtensions::FramebufferBlit);
+
+ caps.npotTexture = f->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures);
+ caps.npotTextureRepeat = f->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat);
+
+ caps.gles = actualFormat.renderableType() == QSurfaceFormat::OpenGLES;
+ if (caps.gles)
+ caps.fixedIndexPrimitiveRestart = caps.ctxMajor >= 3;
+ else
+ caps.fixedIndexPrimitiveRestart = caps.ctxMajor > 4 || (caps.ctxMajor == 4 && caps.ctxMinor >= 3);
+
+ if (caps.fixedIndexPrimitiveRestart)
+ f->glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
+
+ caps.bgraExternalFormat = f->hasOpenGLExtension(QOpenGLExtensions::BGRATextureFormat);
+ caps.bgraInternalFormat = caps.bgraExternalFormat && caps.gles;
+ caps.r8Format = f->hasOpenGLFeature(QOpenGLFunctions::TextureRGFormats);
+ caps.r16Format = f->hasOpenGLExtension(QOpenGLExtensions::Sized16Formats);
+ caps.floatFormats = caps.ctxMajor >= 3;
+ caps.depthTexture = caps.ctxMajor >= 3;
+ caps.packedDepthStencil = f->hasOpenGLExtension(QOpenGLExtensions::PackedDepthStencil);
+ caps.srgbCapableDefaultFramebuffer = f->hasOpenGLExtension(QOpenGLExtensions::SRGBFrameBuffer);
+ caps.coreProfile = actualFormat.profile() == QSurfaceFormat::CoreProfile;
+ caps.uniformBuffers = caps.ctxMajor >= 3 && (caps.gles || caps.ctxMinor >= 1);
+ caps.elementIndexUint = f->hasOpenGLExtension(QOpenGLExtensions::ElementIndexUint);
+
+ nativeHandlesStruct.context = ctx;
+
+ return true;
+}
+
+void QRhiGles2::destroy()
+{
+ if (!f)
+ return;
+
+ ensureContext();
+ executeDeferredReleases();
+
+ if (!importedContext) {
+ delete ctx;
+ ctx = nullptr;
+ }
+
+ f = nullptr;
+}
+
+void QRhiGles2::executeDeferredReleases()
+{
+ for (int i = releaseQueue.count() - 1; i >= 0; --i) {
+ const QRhiGles2::DeferredReleaseEntry &e(releaseQueue[i]);
+ switch (e.type) {
+ case QRhiGles2::DeferredReleaseEntry::Buffer:
+ f->glDeleteBuffers(1, &e.buffer.buffer);
+ break;
+ case QRhiGles2::DeferredReleaseEntry::Pipeline:
+ f->glDeleteProgram(e.pipeline.program);
+ break;
+ case QRhiGles2::DeferredReleaseEntry::Texture:
+ f->glDeleteTextures(1, &e.texture.texture);
+ break;
+ case QRhiGles2::DeferredReleaseEntry::RenderBuffer:
+ f->glDeleteRenderbuffers(1, &e.renderbuffer.renderbuffer);
+ f->glDeleteRenderbuffers(1, &e.renderbuffer.renderbuffer2);
+ break;
+ case QRhiGles2::DeferredReleaseEntry::TextureRenderTarget:
+ f->glDeleteFramebuffers(1, &e.textureRenderTarget.framebuffer);
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ releaseQueue.removeAt(i);
+ }
+}
+
+QVector<int> QRhiGles2::supportedSampleCounts() const
+{
+ if (supportedSampleCountList.isEmpty()) {
+ // 1, 2, 4, 8, ...
+ for (int i = 1; i <= caps.maxSamples; i *= 2)
+ supportedSampleCountList.append(i);
+ }
+ return supportedSampleCountList;
+}
+
+int QRhiGles2::effectiveSampleCount(int sampleCount) const
+{
+ // Stay compatible with QSurfaceFormat and friends where samples == 0 means the same as 1.
+ const int s = qBound(1, sampleCount, 64);
+ if (!supportedSampleCounts().contains(s)) {
+ qWarning("Attempted to set unsupported sample count %d", sampleCount);
+ return 1;
+ }
+ return s;
+}
+
+QRhiSwapChain *QRhiGles2::createSwapChain()
+{
+ return new QGles2SwapChain(this);
+}
+
+QRhiBuffer *QRhiGles2::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size)
+{
+ return new QGles2Buffer(this, type, usage, size);
+}
+
+int QRhiGles2::ubufAlignment() const
+{
+ return 256;
+}
+
+bool QRhiGles2::isYUpInFramebuffer() const
+{
+ return true;
+}
+
+bool QRhiGles2::isYUpInNDC() const
+{
+ return true;
+}
+
+bool QRhiGles2::isClipDepthZeroToOne() const
+{
+ return false;
+}
+
+QMatrix4x4 QRhiGles2::clipSpaceCorrMatrix() const
+{
+ return QMatrix4x4(); // identity
+}
+
+static inline GLenum toGlCompressedTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags)
+{
+ const bool srgb = flags.testFlag(QRhiTexture::sRGB);
+ switch (format) {
+ case QRhiTexture::BC1:
+ return srgb ? 0x8C4C : 0x83F0;
+ case QRhiTexture::BC3:
+ return srgb ? 0x8C4E : 0x83F2;
+ case QRhiTexture::BC5:
+ return srgb ? 0x8C4F : 0x83F3;
+
+ case QRhiTexture::ETC2_RGB8:
+ return srgb ? 0x9275 : 0x9274;
+ case QRhiTexture::ETC2_RGB8A1:
+ return srgb ? 0x9277 : 0x9276;
+ case QRhiTexture::ETC2_RGBA8:
+ return srgb ? 0x9279 : 0x9278;
+
+ case QRhiTexture::ASTC_4x4:
+ return srgb ? 0x93D0 : 0x93B0;
+ case QRhiTexture::ASTC_5x4:
+ return srgb ? 0x93D1 : 0x93B1;
+ case QRhiTexture::ASTC_5x5:
+ return srgb ? 0x93D2 : 0x93B2;
+ case QRhiTexture::ASTC_6x5:
+ return srgb ? 0x93D3 : 0x93B3;
+ case QRhiTexture::ASTC_6x6:
+ return srgb ? 0x93D4 : 0x93B4;
+ case QRhiTexture::ASTC_8x5:
+ return srgb ? 0x93D5 : 0x93B5;
+ case QRhiTexture::ASTC_8x6:
+ return srgb ? 0x93D6 : 0x93B6;
+ case QRhiTexture::ASTC_8x8:
+ return srgb ? 0x93D7 : 0x93B7;
+ case QRhiTexture::ASTC_10x5:
+ return srgb ? 0x93D8 : 0x93B8;
+ case QRhiTexture::ASTC_10x6:
+ return srgb ? 0x93D9 : 0x93B9;
+ case QRhiTexture::ASTC_10x8:
+ return srgb ? 0x93DA : 0x93BA;
+ case QRhiTexture::ASTC_10x10:
+ return srgb ? 0x93DB : 0x93BB;
+ case QRhiTexture::ASTC_12x10:
+ return srgb ? 0x93DC : 0x93BC;
+ case QRhiTexture::ASTC_12x12:
+ return srgb ? 0x93DD : 0x93BD;
+
+ default:
+ return 0; // this is reachable, just return an invalid format
+ }
+}
+
+bool QRhiGles2::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const
+{
+ if (isCompressedFormat(format))
+ return supportedCompressedFormats.contains(toGlCompressedTextureFormat(format, flags));
+
+ switch (format) {
+ case QRhiTexture::D16:
+ Q_FALLTHROUGH();
+ case QRhiTexture::D32F:
+ return caps.depthTexture;
+
+ case QRhiTexture::BGRA8:
+ return caps.bgraExternalFormat;
+
+ case QRhiTexture::R8:
+ return caps.r8Format;
+
+ case QRhiTexture::R16:
+ return caps.r16Format;
+
+ case QRhiTexture::RGBA16F:
+ Q_FALLTHROUGH();
+ case QRhiTexture::RGBA32F:
+ return caps.floatFormats;
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
+bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const
+{
+ switch (feature) {
+ case QRhi::MultisampleTexture:
+ return false;
+ case QRhi::MultisampleRenderBuffer:
+ return caps.msaaRenderBuffer;
+ case QRhi::DebugMarkers:
+ return false;
+ case QRhi::Timestamps:
+ return false;
+ case QRhi::Instancing:
+ return false;
+ case QRhi::CustomInstanceStepRate:
+ return false;
+ case QRhi::PrimitiveRestart:
+ return caps.fixedIndexPrimitiveRestart;
+ case QRhi::NonDynamicUniformBuffers:
+ return true;
+ case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
+ return true;
+ case QRhi::NPOTTextureRepeat:
+ return caps.npotTextureRepeat;
+ case QRhi::RedOrAlpha8IsRed:
+ return caps.coreProfile;
+ case QRhi::ElementIndexUint:
+ return caps.elementIndexUint;
+ case QRhi::Compute:
+ return false;
+ default:
+ Q_UNREACHABLE();
+ return false;
+ }
+}
+
+int QRhiGles2::resourceLimit(QRhi::ResourceLimit limit) const
+{
+ switch (limit) {
+ case QRhi::TextureSizeMin:
+ return 1;
+ case QRhi::TextureSizeMax:
+ return caps.maxTextureSize;
+ case QRhi::MaxColorAttachments:
+ return caps.maxDrawBuffers;
+ case QRhi::FramesInFlight:
+ return 2; // dummy
+ default:
+ Q_UNREACHABLE();
+ return 0;
+ }
+}
+
+const QRhiNativeHandles *QRhiGles2::nativeHandles()
+{
+ return &nativeHandlesStruct;
+}
+
+void QRhiGles2::sendVMemStatsToProfiler()
+{
+ // nothing to do here
+}
+
+void QRhiGles2::makeThreadLocalNativeContextCurrent()
+{
+ if (inFrame && !ofr.active)
+ ensureContext(currentSwapChain->surface);
+ else
+ ensureContext();
+}
+
+QRhiRenderBuffer *QRhiGles2::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags)
+{
+ return new QGles2RenderBuffer(this, type, pixelSize, sampleCount, flags);
+}
+
+QRhiTexture *QRhiGles2::createTexture(QRhiTexture::Format format, const QSize &pixelSize,
+ int sampleCount, QRhiTexture::Flags flags)
+{
+ return new QGles2Texture(this, format, pixelSize, sampleCount, flags);
+}
+
+QRhiSampler *QRhiGles2::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler::AddressMode u, QRhiSampler::AddressMode v)
+{
+ return new QGles2Sampler(this, magFilter, minFilter, mipmapMode, u, v);
+}
+
+QRhiTextureRenderTarget *QRhiGles2::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
+ QRhiTextureRenderTarget::Flags flags)
+{
+ return new QGles2TextureRenderTarget(this, desc, flags);
+}
+
+QRhiGraphicsPipeline *QRhiGles2::createGraphicsPipeline()
+{
+ return new QGles2GraphicsPipeline(this);
+}
+
+QRhiShaderResourceBindings *QRhiGles2::createShaderResourceBindings()
+{
+ return new QGles2ShaderResourceBindings(this);
+}
+
+QRhiComputePipeline *QRhiGles2::createComputePipeline()
+{
+ return new QGles2ComputePipeline(this);
+}
+
+void QRhiGles2::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps)
+{
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
+ QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, ps);
+ const bool pipelineChanged = cbD->currentPipeline != ps || cbD->currentPipelineGeneration != psD->generation;
+
+ if (pipelineChanged) {
+ cbD->currentPipeline = ps;
+ cbD->currentPipelineGeneration = psD->generation;
+
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::BindGraphicsPipeline;
+ cmd.args.bindGraphicsPipeline.ps = ps;
+ cbD->commands.append(cmd);
+ }
+}
+
+void QRhiGles2::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
+{
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
+ Q_ASSERT(cbD->currentPipeline);
+
+ if (!srb)
+ srb = QRHI_RES(QGles2GraphicsPipeline, cbD->currentPipeline)->m_shaderResourceBindings;
+
+ QGles2ShaderResourceBindings *srbD = QRHI_RES(QGles2ShaderResourceBindings, srb);
+ bool hasDynamicOffsetInSrb = false;
+ for (int i = 0, ie = srbD->m_bindings.count(); i != ie; ++i) {
+ const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&srbD->m_bindings[i]);
+ switch (b->type) {
+ case QRhiShaderResourceBinding::UniformBuffer:
+ if (b->u.ubuf.hasDynamicOffset)
+ hasDynamicOffsetInSrb = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ const bool srbChanged = cbD->currentSrb != srb || cbD->currentSrbGeneration != srbD->generation;
+
+ if (srbChanged || hasDynamicOffsetInSrb) {
+ cbD->currentSrb = srb;
+ cbD->currentSrbGeneration = srbD->generation;
+
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::BindShaderResources;
+ cmd.args.bindShaderResources.ps = cbD->currentPipeline;
+ cmd.args.bindShaderResources.srb = srb;
+ cmd.args.bindShaderResources.dynamicOffsetCount = 0;
+ if (hasDynamicOffsetInSrb) {
+ if (dynamicOffsetCount < QGles2CommandBuffer::Command::MAX_UBUF_BINDINGS) {
+ cmd.args.bindShaderResources.dynamicOffsetCount = dynamicOffsetCount;
+ uint *p = cmd.args.bindShaderResources.dynamicOffsetPairs;
+ for (int i = 0; i < dynamicOffsetCount; ++i) {
+ const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
+ *p++ = dynOfs.first;
+ *p++ = dynOfs.second;
+ }
+ } else {
+ qWarning("Too many dynamic offsets (%d, max is %d)",
+ dynamicOffsetCount, QGles2CommandBuffer::Command::MAX_UBUF_BINDINGS);
+ }
+ }
+ cbD->commands.append(cmd);
+ }
+}
+
+void QRhiGles2::setVertexInput(QRhiCommandBuffer *cb,
+ int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
+ QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
+{
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
+
+ for (int i = 0; i < bindingCount; ++i) {
+ QRhiBuffer *buf = bindings[i].first;
+ quint32 ofs = bindings[i].second;
+ QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, buf);
+ Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::VertexBuffer));
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::BindVertexBuffer;
+ cmd.args.bindVertexBuffer.ps = cbD->currentPipeline;
+ cmd.args.bindVertexBuffer.buffer = bufD->buffer;
+ cmd.args.bindVertexBuffer.offset = ofs;
+ cmd.args.bindVertexBuffer.binding = startBinding + i;
+ cbD->commands.append(cmd);
+ }
+
+ if (indexBuf) {
+ QGles2Buffer *ibufD = QRHI_RES(QGles2Buffer, indexBuf);
+ Q_ASSERT(ibufD->m_usage.testFlag(QRhiBuffer::IndexBuffer));
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::BindIndexBuffer;
+ cmd.args.bindIndexBuffer.buffer = ibufD->buffer;
+ cmd.args.bindIndexBuffer.offset = indexOffset;
+ cmd.args.bindIndexBuffer.type = indexFormat == QRhiCommandBuffer::IndexUInt16 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
+ cbD->commands.append(cmd);
+ }
+}
+
+void QRhiGles2::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport)
+{
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
+
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::Viewport;
+ const std::array<float, 4> r = viewport.viewport();
+ cmd.args.viewport.x = qMax(0.0f, r[0]);
+ cmd.args.viewport.y = qMax(0.0f, r[1]);
+ cmd.args.viewport.w = r[2];
+ cmd.args.viewport.h = r[3];
+ cmd.args.viewport.d0 = viewport.minDepth();
+ cmd.args.viewport.d1 = viewport.maxDepth();
+ cbD->commands.append(cmd);
+}
+
+void QRhiGles2::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor)
+{
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
+
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::Scissor;
+ const std::array<int, 4> r = scissor.scissor();
+ cmd.args.scissor.x = qMax(0, r[0]);
+ cmd.args.scissor.y = qMax(0, r[1]);
+ cmd.args.scissor.w = r[2];
+ cmd.args.scissor.h = r[3];
+ cbD->commands.append(cmd);
+}
+
+void QRhiGles2::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
+{
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
+
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::BlendConstants;
+ cmd.args.blendConstants.r = c.redF();
+ cmd.args.blendConstants.g = c.greenF();
+ cmd.args.blendConstants.b = c.blueF();
+ cmd.args.blendConstants.a = c.alphaF();
+ cbD->commands.append(cmd);
+}
+
+void QRhiGles2::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
+{
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
+
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::StencilRef;
+ cmd.args.stencilRef.ref = refValue;
+ cmd.args.stencilRef.ps = cbD->currentPipeline;
+ cbD->commands.append(cmd);
+}
+
+void QRhiGles2::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
+ quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
+{
+ Q_UNUSED(instanceCount); // no instancing
+ Q_UNUSED(firstInstance);
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
+
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::Draw;
+ cmd.args.draw.ps = cbD->currentPipeline;
+ cmd.args.draw.vertexCount = vertexCount;
+ cmd.args.draw.firstVertex = firstVertex;
+ cbD->commands.append(cmd);
+}
+
+void QRhiGles2::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
+ quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
+{
+ Q_UNUSED(instanceCount); // no instancing
+ Q_UNUSED(firstInstance);
+ Q_UNUSED(vertexOffset); // no glDrawElementsBaseVertex
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
+
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::DrawIndexed;
+ cmd.args.drawIndexed.ps = cbD->currentPipeline;
+ cmd.args.drawIndexed.indexCount = indexCount;
+ cmd.args.drawIndexed.firstIndex = firstIndex;
+ cbD->commands.append(cmd);
+}
+
+void QRhiGles2::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
+{
+ if (!debugMarkers)
+ return;
+
+ Q_UNUSED(cb);
+ Q_UNUSED(name);
+}
+
+void QRhiGles2::debugMarkEnd(QRhiCommandBuffer *cb)
+{
+ if (!debugMarkers)
+ return;
+
+ Q_UNUSED(cb);
+}
+
+void QRhiGles2::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
+{
+ if (!debugMarkers)
+ return;
+
+ Q_UNUSED(cb);
+ Q_UNUSED(msg);
+}
+
+const QRhiNativeHandles *QRhiGles2::nativeHandles(QRhiCommandBuffer *cb)
+{
+ Q_UNUSED(cb);
+ return nullptr;
+}
+
+void QRhiGles2::beginExternal(QRhiCommandBuffer *cb)
+{
+ Q_UNUSED(cb);
+ flushCommandBuffer(); // also ensures the context is current
+}
+
+void QRhiGles2::endExternal(QRhiCommandBuffer *cb)
+{
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+ Q_ASSERT(cbD->commands.isEmpty());
+ cbD->resetCachedState();
+ if (cbD->currentTarget)
+ enqueueBindFramebuffer(cbD->currentTarget, cbD);
+}
+
+static void addBoundaryCommand(QGles2CommandBuffer *cb, QGles2CommandBuffer::Command::Cmd type)
+{
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = type;
+ cb->commands.append(cmd);
+}
+
+QRhi::FrameOpResult QRhiGles2::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags)
+{
+ Q_UNUSED(flags);
+
+ QGles2SwapChain *swapChainD = QRHI_RES(QGles2SwapChain, swapChain);
+ if (!ensureContext(swapChainD->surface))
+ return QRhi::FrameOpError;
+
+ currentSwapChain = swapChainD;
+
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+ QRHI_PROF_F(beginSwapChainFrame(swapChain));
+
+ executeDeferredReleases();
+ swapChainD->cb.resetState();
+
+ addBoundaryCommand(&swapChainD->cb, QGles2CommandBuffer::Command::BeginFrame);
+
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiGles2::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags)
+{
+ QGles2SwapChain *swapChainD = QRHI_RES(QGles2SwapChain, swapChain);
+ Q_ASSERT(currentSwapChain == swapChainD);
+
+ addBoundaryCommand(&swapChainD->cb, QGles2CommandBuffer::Command::EndFrame);
+
+ if (!ensureContext(swapChainD->surface))
+ return QRhi::FrameOpError;
+
+ executeCommandBuffer(&swapChainD->cb);
+
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+ // this must be done before the swap
+ QRHI_PROF_F(endSwapChainFrame(swapChain, swapChainD->frameCount + 1));
+
+ if (swapChainD->surface && !flags.testFlag(QRhi::SkipPresent)) {
+ ctx->swapBuffers(swapChainD->surface);
+ needsMakeCurrent = true;
+ } else {
+ f->glFlush();
+ }
+
+ swapChainD->frameCount += 1;
+ currentSwapChain = nullptr;
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiGles2::beginOffscreenFrame(QRhiCommandBuffer **cb)
+{
+ if (!ensureContext())
+ return QRhi::FrameOpError;
+
+ ofr.active = true;
+
+ executeDeferredReleases();
+ ofr.cbWrapper.resetState();
+
+ addBoundaryCommand(&ofr.cbWrapper, QGles2CommandBuffer::Command::BeginFrame);
+ *cb = &ofr.cbWrapper;
+
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiGles2::endOffscreenFrame()
+{
+ Q_ASSERT(ofr.active);
+ ofr.active = false;
+
+ addBoundaryCommand(&ofr.cbWrapper, QGles2CommandBuffer::Command::EndFrame);
+
+ if (!ensureContext())
+ return QRhi::FrameOpError;
+
+ executeCommandBuffer(&ofr.cbWrapper);
+
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiGles2::finish()
+{
+ return inFrame ? flushCommandBuffer() : QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiGles2::flushCommandBuffer()
+{
+ if (ofr.active) {
+ Q_ASSERT(!currentSwapChain);
+ if (!ensureContext())
+ return QRhi::FrameOpError;
+ executeCommandBuffer(&ofr.cbWrapper);
+ ofr.cbWrapper.resetCommands();
+ } else {
+ Q_ASSERT(currentSwapChain);
+ if (!ensureContext(currentSwapChain->surface))
+ return QRhi::FrameOpError;
+ executeCommandBuffer(&currentSwapChain->cb);
+ currentSwapChain->cb.resetCommands();
+ }
+ return QRhi::FrameOpSuccess;
+}
+
+void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cbD,
+ int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc)
+{
+ const bool isCompressed = isCompressedFormat(texD->m_format);
+ const bool isCubeMap = texD->m_flags.testFlag(QRhiTexture::CubeMap);
+ const GLenum faceTargetBase = isCubeMap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
+ const QPoint dp = subresDesc.destinationTopLeft();
+ const QByteArray rawData = subresDesc.data();
+ if (!subresDesc.image().isNull()) {
+ QImage img = subresDesc.image();
+ QSize size = img.size();
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::SubImage;
+ if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
+ const QPoint sp = subresDesc.sourceTopLeft();
+ if (!subresDesc.sourceSize().isEmpty())
+ size = subresDesc.sourceSize();
+ img = img.copy(sp.x(), sp.y(), size.width(), size.height());
+ }
+ cmd.args.subImage.target = texD->target;
+ cmd.args.subImage.texture = texD->texture;
+ cmd.args.subImage.faceTarget = faceTargetBase + layer;
+ cmd.args.subImage.level = level;
+ cmd.args.subImage.dx = dp.x();
+ cmd.args.subImage.dy = dp.y();
+ cmd.args.subImage.w = size.width();
+ cmd.args.subImage.h = size.height();
+ cmd.args.subImage.glformat = texD->glformat;
+ cmd.args.subImage.gltype = texD->gltype;
+ cmd.args.subImage.rowStartAlign = 4;
+ cmd.args.subImage.data = cbD->retainImage(img);
+ cbD->commands.append(cmd);
+ } else if (!rawData.isEmpty() && isCompressed) {
+ const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize)
+ : subresDesc.sourceSize();
+ if (texD->specified) {
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::CompressedSubImage;
+ cmd.args.compressedSubImage.target = texD->target;
+ cmd.args.compressedSubImage.texture = texD->texture;
+ cmd.args.compressedSubImage.faceTarget = faceTargetBase + layer;
+ cmd.args.compressedSubImage.level = level;
+ cmd.args.compressedSubImage.dx = dp.x();
+ cmd.args.compressedSubImage.dy = dp.y();
+ cmd.args.compressedSubImage.w = size.width();
+ cmd.args.compressedSubImage.h = size.height();
+ cmd.args.compressedSubImage.glintformat = texD->glintformat;
+ cmd.args.compressedSubImage.size = rawData.size();
+ cmd.args.compressedSubImage.data = cbD->retainData(rawData);
+ cbD->commands.append(cmd);
+ } else {
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::CompressedImage;
+ cmd.args.compressedImage.target = texD->target;
+ cmd.args.compressedImage.texture = texD->texture;
+ cmd.args.compressedImage.faceTarget = faceTargetBase + layer;
+ cmd.args.compressedImage.level = level;
+ cmd.args.compressedImage.glintformat = texD->glintformat;
+ cmd.args.compressedImage.w = size.width();
+ cmd.args.compressedImage.h = size.height();
+ cmd.args.compressedImage.size = rawData.size();
+ cmd.args.compressedImage.data = cbD->retainData(rawData);
+ cbD->commands.append(cmd);
+ }
+ } else if (!rawData.isEmpty()) {
+ const QSize size = subresDesc.sourceSize().isEmpty() ? q->sizeForMipLevel(level, texD->m_pixelSize)
+ : subresDesc.sourceSize();
+ quint32 bpl = 0;
+ textureFormatInfo(texD->m_format, size, &bpl, nullptr);
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::SubImage;
+ cmd.args.subImage.target = texD->target;
+ cmd.args.subImage.texture = texD->texture;
+ cmd.args.subImage.faceTarget = faceTargetBase + layer;
+ cmd.args.subImage.level = level;
+ cmd.args.subImage.dx = dp.x();
+ cmd.args.subImage.dy = dp.y();
+ cmd.args.subImage.w = size.width();
+ cmd.args.subImage.h = size.height();
+ cmd.args.subImage.glformat = texD->glformat;
+ cmd.args.subImage.gltype = texD->gltype;
+ // Default unpack alignment (row start aligment
+ // requirement) is 4. QImage guarantees 4 byte aligned
+ // row starts, but our raw data here does not.
+ cmd.args.subImage.rowStartAlign = (bpl & 3) ? 1 : 4;
+ cmd.args.subImage.data = cbD->retainData(rawData);
+ cbD->commands.append(cmd);
+ } else {
+ qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
+ }
+}
+
+void QRhiGles2::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+ QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
+
+ for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : ud->dynamicBufferUpdates) {
+ QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf);
+ Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
+ if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
+ memcpy(bufD->ubuf.data() + u.offset, u.data.constData(), u.data.size());
+ } else {
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::BufferSubData;
+ cmd.args.bufferSubData.target = bufD->target;
+ cmd.args.bufferSubData.buffer = bufD->buffer;
+ cmd.args.bufferSubData.offset = u.offset;
+ cmd.args.bufferSubData.size = u.data.size();
+ cmd.args.bufferSubData.data = cbD->retainData(u.data);
+ cbD->commands.append(cmd);
+ }
+ }
+
+ for (const QRhiResourceUpdateBatchPrivate::StaticBufferUpload &u : ud->staticBufferUploads) {
+ QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, u.buf);
+ Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
+ Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
+ if (bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
+ memcpy(bufD->ubuf.data() + u.offset, u.data.constData(), u.data.size());
+ } else {
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::BufferSubData;
+ cmd.args.bufferSubData.target = bufD->target;
+ cmd.args.bufferSubData.buffer = bufD->buffer;
+ cmd.args.bufferSubData.offset = u.offset;
+ cmd.args.bufferSubData.size = u.data.size();
+ cmd.args.bufferSubData.data = cbD->retainData(u.data);
+ cbD->commands.append(cmd);
+ }
+ }
+
+ for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) {
+ if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
+ QGles2Texture *texD = QRHI_RES(QGles2Texture, u.upload.tex);
+ for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
+ for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
+ for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.upload.subresDesc[layer][level]))
+ enqueueSubresUpload(texD, cbD, layer, level, subresDesc);
+ }
+ }
+ texD->specified = true;
+ } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) {
+ Q_ASSERT(u.copy.src && u.copy.dst);
+ QGles2Texture *srcD = QRHI_RES(QGles2Texture, u.copy.src);
+ QGles2Texture *dstD = QRHI_RES(QGles2Texture, u.copy.dst);
+
+ const QSize size = u.copy.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : u.copy.desc.pixelSize();
+ // do not translate coordinates, even if sp is bottom-left from gl's pov
+ const QPoint sp = u.copy.desc.sourceTopLeft();
+ const QPoint dp = u.copy.desc.destinationTopLeft();
+
+ const GLenum srcFaceTargetBase = srcD->m_flags.testFlag(QRhiTexture::CubeMap)
+ ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : srcD->target;
+ const GLenum dstFaceTargetBase = dstD->m_flags.testFlag(QRhiTexture::CubeMap)
+ ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : dstD->target;
+
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::CopyTex;
+
+ cmd.args.copyTex.srcFaceTarget = srcFaceTargetBase + u.copy.desc.sourceLayer();
+ cmd.args.copyTex.srcTexture = srcD->texture;
+ cmd.args.copyTex.srcLevel = u.copy.desc.sourceLevel();
+ cmd.args.copyTex.srcX = sp.x();
+ cmd.args.copyTex.srcY = sp.y();
+
+ cmd.args.copyTex.dstTarget = dstD->target;
+ cmd.args.copyTex.dstTexture = dstD->texture;
+ cmd.args.copyTex.dstFaceTarget = dstFaceTargetBase + u.copy.desc.destinationLayer();
+ cmd.args.copyTex.dstLevel = u.copy.desc.destinationLevel();
+ cmd.args.copyTex.dstX = dp.x();
+ cmd.args.copyTex.dstY = dp.y();
+
+ cmd.args.copyTex.w = size.width();
+ cmd.args.copyTex.h = size.height();
+
+ cbD->commands.append(cmd);
+ } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::ReadPixels;
+ cmd.args.readPixels.result = u.read.result;
+ QGles2Texture *texD = QRHI_RES(QGles2Texture, u.read.rb.texture());
+ cmd.args.readPixels.texture = texD ? texD->texture : 0;
+ if (texD) {
+ cmd.args.readPixels.w = texD->m_pixelSize.width();
+ cmd.args.readPixels.h = texD->m_pixelSize.height();
+ cmd.args.readPixels.format = texD->m_format;
+ const GLenum faceTargetBase = texD->m_flags.testFlag(QRhiTexture::CubeMap)
+ ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
+ cmd.args.readPixels.readTarget = faceTargetBase + u.read.rb.layer();
+ cmd.args.readPixels.level = u.read.rb.level();
+ }
+ cbD->commands.append(cmd);
+ } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::MipGen) {
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::GenMip;
+ QGles2Texture *texD = QRHI_RES(QGles2Texture, u.mipgen.tex);
+ cmd.args.genMip.target = texD->target;
+ cmd.args.genMip.texture = texD->texture;
+ cbD->commands.append(cmd);
+ }
+ }
+
+ ud->free();
+}
+
+static inline GLenum toGlTopology(QRhiGraphicsPipeline::Topology t)
+{
+ switch (t) {
+ case QRhiGraphicsPipeline::Triangles:
+ return GL_TRIANGLES;
+ case QRhiGraphicsPipeline::TriangleStrip:
+ return GL_TRIANGLE_STRIP;
+ case QRhiGraphicsPipeline::Lines:
+ return GL_LINES;
+ case QRhiGraphicsPipeline::LineStrip:
+ return GL_LINE_STRIP;
+ case QRhiGraphicsPipeline::Points:
+ return GL_POINTS;
+ default:
+ Q_UNREACHABLE();
+ return GL_TRIANGLES;
+ }
+}
+
+static inline GLenum toGlCullMode(QRhiGraphicsPipeline::CullMode c)
+{
+ switch (c) {
+ case QRhiGraphicsPipeline::Front:
+ return GL_FRONT;
+ case QRhiGraphicsPipeline::Back:
+ return GL_BACK;
+ default:
+ Q_UNREACHABLE();
+ return GL_BACK;
+ }
+}
+
+static inline GLenum toGlFrontFace(QRhiGraphicsPipeline::FrontFace f)
+{
+ switch (f) {
+ case QRhiGraphicsPipeline::CCW:
+ return GL_CCW;
+ case QRhiGraphicsPipeline::CW:
+ return GL_CW;
+ default:
+ Q_UNREACHABLE();
+ return GL_CCW;
+ }
+}
+
+static inline GLenum toGlBlendFactor(QRhiGraphicsPipeline::BlendFactor f)
+{
+ switch (f) {
+ case QRhiGraphicsPipeline::Zero:
+ return GL_ZERO;
+ case QRhiGraphicsPipeline::One:
+ return GL_ONE;
+ case QRhiGraphicsPipeline::SrcColor:
+ return GL_SRC_COLOR;
+ case QRhiGraphicsPipeline::OneMinusSrcColor:
+ return GL_ONE_MINUS_SRC_COLOR;
+ case QRhiGraphicsPipeline::DstColor:
+ return GL_DST_COLOR;
+ case QRhiGraphicsPipeline::OneMinusDstColor:
+ return GL_ONE_MINUS_DST_COLOR;
+ case QRhiGraphicsPipeline::SrcAlpha:
+ return GL_SRC_ALPHA;
+ case QRhiGraphicsPipeline::OneMinusSrcAlpha:
+ return GL_ONE_MINUS_SRC_ALPHA;
+ case QRhiGraphicsPipeline::DstAlpha:
+ return GL_DST_ALPHA;
+ case QRhiGraphicsPipeline::OneMinusDstAlpha:
+ return GL_ONE_MINUS_DST_ALPHA;
+ case QRhiGraphicsPipeline::ConstantColor:
+ return GL_CONSTANT_COLOR;
+ case QRhiGraphicsPipeline::OneMinusConstantColor:
+ return GL_ONE_MINUS_CONSTANT_COLOR;
+ case QRhiGraphicsPipeline::ConstantAlpha:
+ return GL_CONSTANT_ALPHA;
+ case QRhiGraphicsPipeline::OneMinusConstantAlpha:
+ return GL_ONE_MINUS_CONSTANT_ALPHA;
+ case QRhiGraphicsPipeline::SrcAlphaSaturate:
+ return GL_SRC_ALPHA_SATURATE;
+ case QRhiGraphicsPipeline::Src1Color:
+ Q_FALLTHROUGH();
+ case QRhiGraphicsPipeline::OneMinusSrc1Color:
+ Q_FALLTHROUGH();
+ case QRhiGraphicsPipeline::Src1Alpha:
+ Q_FALLTHROUGH();
+ case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
+ qWarning("Unsupported blend factor %d", f);
+ return GL_ZERO;
+ default:
+ Q_UNREACHABLE();
+ return GL_ZERO;
+ }
+}
+
+static inline GLenum toGlBlendOp(QRhiGraphicsPipeline::BlendOp op)
+{
+ switch (op) {
+ case QRhiGraphicsPipeline::Add:
+ return GL_FUNC_ADD;
+ case QRhiGraphicsPipeline::Subtract:
+ return GL_FUNC_SUBTRACT;
+ case QRhiGraphicsPipeline::ReverseSubtract:
+ return GL_FUNC_REVERSE_SUBTRACT;
+ case QRhiGraphicsPipeline::Min:
+ return GL_MIN;
+ case QRhiGraphicsPipeline::Max:
+ return GL_MAX;
+ default:
+ Q_UNREACHABLE();
+ return GL_FUNC_ADD;
+ }
+}
+
+static inline GLenum toGlCompareOp(QRhiGraphicsPipeline::CompareOp op)
+{
+ switch (op) {
+ case QRhiGraphicsPipeline::Never:
+ return GL_NEVER;
+ case QRhiGraphicsPipeline::Less:
+ return GL_LESS;
+ case QRhiGraphicsPipeline::Equal:
+ return GL_EQUAL;
+ case QRhiGraphicsPipeline::LessOrEqual:
+ return GL_LEQUAL;
+ case QRhiGraphicsPipeline::Greater:
+ return GL_GREATER;
+ case QRhiGraphicsPipeline::NotEqual:
+ return GL_NOTEQUAL;
+ case QRhiGraphicsPipeline::GreaterOrEqual:
+ return GL_GEQUAL;
+ case QRhiGraphicsPipeline::Always:
+ return GL_ALWAYS;
+ default:
+ Q_UNREACHABLE();
+ return GL_ALWAYS;
+ }
+}
+
+static inline GLenum toGlStencilOp(QRhiGraphicsPipeline::StencilOp op)
+{
+ switch (op) {
+ case QRhiGraphicsPipeline::StencilZero:
+ return GL_ZERO;
+ case QRhiGraphicsPipeline::Keep:
+ return GL_KEEP;
+ case QRhiGraphicsPipeline::Replace:
+ return GL_REPLACE;
+ case QRhiGraphicsPipeline::IncrementAndClamp:
+ return GL_INCR;
+ case QRhiGraphicsPipeline::DecrementAndClamp:
+ return GL_DECR;
+ case QRhiGraphicsPipeline::Invert:
+ return GL_INVERT;
+ case QRhiGraphicsPipeline::IncrementAndWrap:
+ return GL_INCR_WRAP;
+ case QRhiGraphicsPipeline::DecrementAndWrap:
+ return GL_DECR_WRAP;
+ default:
+ Q_UNREACHABLE();
+ return GL_KEEP;
+ }
+}
+
+static inline GLenum toGlMinFilter(QRhiSampler::Filter f, QRhiSampler::Filter m)
+{
+ switch (f) {
+ case QRhiSampler::Nearest:
+ if (m == QRhiSampler::None)
+ return GL_NEAREST;
+ else
+ return m == QRhiSampler::Nearest ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_LINEAR;
+ case QRhiSampler::Linear:
+ if (m == QRhiSampler::None)
+ return GL_LINEAR;
+ else
+ return m == QRhiSampler::Nearest ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR_MIPMAP_LINEAR;
+ default:
+ Q_UNREACHABLE();
+ return GL_LINEAR;
+ }
+}
+
+static inline GLenum toGlMagFilter(QRhiSampler::Filter f)
+{
+ switch (f) {
+ case QRhiSampler::Nearest:
+ return GL_NEAREST;
+ case QRhiSampler::Linear:
+ return GL_LINEAR;
+ default:
+ Q_UNREACHABLE();
+ return GL_LINEAR;
+ }
+}
+
+static inline GLenum toGlWrapMode(QRhiSampler::AddressMode m)
+{
+ switch (m) {
+ case QRhiSampler::Repeat:
+ return GL_REPEAT;
+ case QRhiSampler::ClampToEdge:
+ return GL_CLAMP_TO_EDGE;
+ case QRhiSampler::Mirror:
+ return GL_MIRRORED_REPEAT;
+ case QRhiSampler::MirrorOnce:
+ Q_FALLTHROUGH();
+ case QRhiSampler::Border:
+ qWarning("Unsupported wrap mode %d", m);
+ return GL_CLAMP_TO_EDGE;
+ default:
+ Q_UNREACHABLE();
+ return GL_CLAMP_TO_EDGE;
+ }
+}
+
+static inline GLenum toGlTextureCompareFunc(QRhiSampler::CompareOp op)
+{
+ switch (op) {
+ case QRhiSampler::Never:
+ return GL_NEVER;
+ case QRhiSampler::Less:
+ return GL_LESS;
+ case QRhiSampler::Equal:
+ return GL_EQUAL;
+ case QRhiSampler::LessOrEqual:
+ return GL_LEQUAL;
+ case QRhiSampler::Greater:
+ return GL_GREATER;
+ case QRhiSampler::NotEqual:
+ return GL_NOTEQUAL;
+ case QRhiSampler::GreaterOrEqual:
+ return GL_GEQUAL;
+ case QRhiSampler::Always:
+ return GL_ALWAYS;
+ default:
+ Q_UNREACHABLE();
+ return GL_NEVER;
+ }
+}
+
+void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
+{
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+ GLenum indexType = GL_UNSIGNED_SHORT;
+ quint32 indexStride = sizeof(quint16);
+ quint32 indexOffset = 0;
+
+ for (const QGles2CommandBuffer::Command &cmd : qAsConst(cbD->commands)) {
+ switch (cmd.cmd) {
+ case QGles2CommandBuffer::Command::BeginFrame:
+ if (caps.coreProfile) {
+ if (!vao)
+ f->glGenVertexArrays(1, &vao);
+ f->glBindVertexArray(vao);
+ }
+ break;
+ case QGles2CommandBuffer::Command::EndFrame:
+ if (vao)
+ f->glBindVertexArray(0);
+ break;
+ case QGles2CommandBuffer::Command::Viewport:
+ f->glViewport(cmd.args.viewport.x, cmd.args.viewport.y, cmd.args.viewport.w, cmd.args.viewport.h);
+ f->glDepthRangef(cmd.args.viewport.d0, cmd.args.viewport.d1);
+ break;
+ case QGles2CommandBuffer::Command::Scissor:
+ f->glScissor(cmd.args.scissor.x, cmd.args.scissor.y, cmd.args.scissor.w, cmd.args.scissor.h);
+ break;
+ case QGles2CommandBuffer::Command::BlendConstants:
+ f->glBlendColor(cmd.args.blendConstants.r, cmd.args.blendConstants.g, cmd.args.blendConstants.b, cmd.args.blendConstants.a);
+ break;
+ case QGles2CommandBuffer::Command::StencilRef:
+ {
+ QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.stencilRef.ps);
+ if (psD) {
+ f->glStencilFuncSeparate(GL_FRONT, toGlCompareOp(psD->m_stencilFront.compareOp), cmd.args.stencilRef.ref, psD->m_stencilReadMask);
+ f->glStencilFuncSeparate(GL_BACK, toGlCompareOp(psD->m_stencilBack.compareOp), cmd.args.stencilRef.ref, psD->m_stencilReadMask);
+ } else {
+ qWarning("No graphics pipeline active for setStencilRef; ignored");
+ }
+ }
+ break;
+ case QGles2CommandBuffer::Command::BindVertexBuffer:
+ {
+ QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.bindVertexBuffer.ps);
+ if (psD) {
+ const QVector<QRhiVertexInputBinding> bindings = psD->m_vertexInputLayout.bindings();
+ const QVector<QRhiVertexInputAttribute> attributes = psD->m_vertexInputLayout.attributes();
+ for (const QRhiVertexInputAttribute &a : attributes) {
+ if (a.binding() != cmd.args.bindVertexBuffer.binding)
+ continue;
+
+ // we do not support more than one vertex buffer
+ f->glBindBuffer(GL_ARRAY_BUFFER, cmd.args.bindVertexBuffer.buffer);
+
+ const int stride = bindings[a.binding()].stride();
+ int size = 1;
+ GLenum type = GL_FLOAT;
+ bool normalize = false;
+ switch (a.format()) {
+ case QRhiVertexInputAttribute::Float4:
+ type = GL_FLOAT;
+ size = 4;
+ break;
+ case QRhiVertexInputAttribute::Float3:
+ type = GL_FLOAT;
+ size = 3;
+ break;
+ case QRhiVertexInputAttribute::Float2:
+ type = GL_FLOAT;
+ size = 2;
+ break;
+ case QRhiVertexInputAttribute::Float:
+ type = GL_FLOAT;
+ size = 1;
+ break;
+ case QRhiVertexInputAttribute::UNormByte4:
+ type = GL_UNSIGNED_BYTE;
+ normalize = true;
+ size = 4;
+ break;
+ case QRhiVertexInputAttribute::UNormByte2:
+ type = GL_UNSIGNED_BYTE;
+ normalize = true;
+ size = 2;
+ break;
+ case QRhiVertexInputAttribute::UNormByte:
+ type = GL_UNSIGNED_BYTE;
+ normalize = true;
+ size = 1;
+ break;
+ default:
+ break;
+ }
+ quint32 ofs = a.offset() + cmd.args.bindVertexBuffer.offset;
+ f->glVertexAttribPointer(a.location(), size, type, normalize, stride,
+ reinterpret_cast<const GLvoid *>(quintptr(ofs)));
+ f->glEnableVertexAttribArray(a.location());
+ }
+ } else {
+ qWarning("No graphics pipeline active for setVertexInput; ignored");
+ }
+ }
+ break;
+ case QGles2CommandBuffer::Command::BindIndexBuffer:
+ indexType = cmd.args.bindIndexBuffer.type;
+ indexStride = indexType == GL_UNSIGNED_SHORT ? sizeof(quint16) : sizeof(quint32);
+ indexOffset = cmd.args.bindIndexBuffer.offset;
+ f->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cmd.args.bindIndexBuffer.buffer);
+ break;
+ case QGles2CommandBuffer::Command::Draw:
+ {
+ QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.draw.ps);
+ if (psD)
+ f->glDrawArrays(psD->drawMode, cmd.args.draw.firstVertex, cmd.args.draw.vertexCount);
+ else
+ qWarning("No graphics pipeline active for draw; ignored");
+ }
+ break;
+ case QGles2CommandBuffer::Command::DrawIndexed:
+ {
+ QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, cmd.args.drawIndexed.ps);
+ if (psD) {
+ quint32 ofs = cmd.args.drawIndexed.firstIndex * indexStride + indexOffset;
+ f->glDrawElements(psD->drawMode,
+ cmd.args.drawIndexed.indexCount,
+ indexType,
+ reinterpret_cast<const GLvoid *>(quintptr(ofs)));
+ } else {
+ qWarning("No graphics pipeline active for drawIndexed; ignored");
+ }
+ }
+ break;
+ case QGles2CommandBuffer::Command::BindGraphicsPipeline:
+ executeBindGraphicsPipeline(cmd.args.bindGraphicsPipeline.ps);
+ break;
+ case QGles2CommandBuffer::Command::BindShaderResources:
+ bindShaderResources(cmd.args.bindShaderResources.ps,
+ cmd.args.bindShaderResources.srb,
+ cmd.args.bindShaderResources.dynamicOffsetPairs,
+ cmd.args.bindShaderResources.dynamicOffsetCount);
+ break;
+ case QGles2CommandBuffer::Command::BindFramebuffer:
+ if (cmd.args.bindFramebuffer.fbo) {
+ f->glBindFramebuffer(GL_FRAMEBUFFER, cmd.args.bindFramebuffer.fbo);
+ if (caps.maxDrawBuffers > 1) {
+ const int colorAttCount = cmd.args.bindFramebuffer.colorAttCount;
+ QVarLengthArray<GLenum, 8> bufs;
+ for (int i = 0; i < colorAttCount; ++i)
+ bufs.append(GL_COLOR_ATTACHMENT0 + i);
+ f->glDrawBuffers(colorAttCount, bufs.constData());
+ }
+ } else {
+ f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
+ if (caps.maxDrawBuffers > 1) {
+ GLenum bufs = GL_BACK;
+ f->glDrawBuffers(1, &bufs);
+ }
+ }
+ if (caps.srgbCapableDefaultFramebuffer) {
+ if (cmd.args.bindFramebuffer.srgb)
+ f->glEnable(GL_FRAMEBUFFER_SRGB);
+ else
+ f->glDisable(GL_FRAMEBUFFER_SRGB);
+ }
+ break;
+ case QGles2CommandBuffer::Command::Clear:
+ f->glDisable(GL_SCISSOR_TEST);
+ if (cmd.args.clear.mask & GL_COLOR_BUFFER_BIT) {
+ f->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ f->glClearColor(cmd.args.clear.c[0], cmd.args.clear.c[1], cmd.args.clear.c[2], cmd.args.clear.c[3]);
+ }
+ if (cmd.args.clear.mask & GL_DEPTH_BUFFER_BIT) {
+ f->glDepthMask(GL_TRUE);
+ f->glClearDepthf(cmd.args.clear.d);
+ }
+ if (cmd.args.clear.mask & GL_STENCIL_BUFFER_BIT)
+ f->glClearStencil(cmd.args.clear.s);
+ f->glClear(cmd.args.clear.mask);
+ break;
+ case QGles2CommandBuffer::Command::BufferSubData:
+ f->glBindBuffer(cmd.args.bufferSubData.target, cmd.args.bufferSubData.buffer);
+ f->glBufferSubData(cmd.args.bufferSubData.target, cmd.args.bufferSubData.offset, cmd.args.bufferSubData.size,
+ cmd.args.bufferSubData.data);
+ break;
+ case QGles2CommandBuffer::Command::CopyTex:
+ {
+ GLuint fbo;
+ f->glGenFramebuffers(1, &fbo);
+ f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ cmd.args.copyTex.srcFaceTarget, cmd.args.copyTex.srcTexture, cmd.args.copyTex.srcLevel);
+ f->glBindTexture(cmd.args.copyTex.dstTarget, cmd.args.copyTex.dstTexture);
+ f->glCopyTexSubImage2D(cmd.args.copyTex.dstFaceTarget, cmd.args.copyTex.dstLevel,
+ cmd.args.copyTex.dstX, cmd.args.copyTex.dstY,
+ cmd.args.copyTex.srcX, cmd.args.copyTex.srcY,
+ cmd.args.copyTex.w, cmd.args.copyTex.h);
+ f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
+ f->glDeleteFramebuffers(1, &fbo);
+ }
+ break;
+ case QGles2CommandBuffer::Command::ReadPixels:
+ {
+ QRhiReadbackResult *result = cmd.args.readPixels.result;
+ GLuint tex = cmd.args.readPixels.texture;
+ GLuint fbo = 0;
+ if (tex) {
+ result->pixelSize = QSize(cmd.args.readPixels.w, cmd.args.readPixels.h);
+ result->format = cmd.args.readPixels.format;
+ f->glGenFramebuffers(1, &fbo);
+ f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ cmd.args.readPixels.readTarget, cmd.args.readPixels.texture, cmd.args.readPixels.level);
+ } else {
+ result->pixelSize = currentSwapChain->pixelSize;
+ result->format = QRhiTexture::RGBA8;
+ // readPixels handles multisample resolving implicitly
+ }
+ result->data.resize(result->pixelSize.width() * result->pixelSize.height() * 4);
+ // With GLES (2.0?) GL_RGBA is the only mandated readback format, so stick with it.
+ f->glReadPixels(0, 0, result->pixelSize.width(), result->pixelSize.height(),
+ GL_RGBA, GL_UNSIGNED_BYTE,
+ result->data.data());
+ if (fbo) {
+ f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
+ f->glDeleteFramebuffers(1, &fbo);
+ }
+ if (result->completed)
+ result->completed();
+ }
+ break;
+ case QGles2CommandBuffer::Command::SubImage:
+ f->glBindTexture(cmd.args.subImage.target, cmd.args.subImage.texture);
+ if (cmd.args.subImage.rowStartAlign != 4)
+ f->glPixelStorei(GL_UNPACK_ALIGNMENT, cmd.args.subImage.rowStartAlign);
+ f->glTexSubImage2D(cmd.args.subImage.faceTarget, cmd.args.subImage.level,
+ cmd.args.subImage.dx, cmd.args.subImage.dy,
+ cmd.args.subImage.w, cmd.args.subImage.h,
+ cmd.args.subImage.glformat, cmd.args.subImage.gltype,
+ cmd.args.subImage.data);
+ if (cmd.args.subImage.rowStartAlign != 4)
+ f->glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+ break;
+ case QGles2CommandBuffer::Command::CompressedImage:
+ f->glBindTexture(cmd.args.compressedImage.target, cmd.args.compressedImage.texture);
+ f->glCompressedTexImage2D(cmd.args.compressedImage.faceTarget, cmd.args.compressedImage.level,
+ cmd.args.compressedImage.glintformat,
+ cmd.args.compressedImage.w, cmd.args.compressedImage.h, 0,
+ cmd.args.compressedImage.size, cmd.args.compressedImage.data);
+ break;
+ case QGles2CommandBuffer::Command::CompressedSubImage:
+ f->glBindTexture(cmd.args.compressedSubImage.target, cmd.args.compressedSubImage.texture);
+ f->glCompressedTexSubImage2D(cmd.args.compressedSubImage.faceTarget, cmd.args.compressedSubImage.level,
+ cmd.args.compressedSubImage.dx, cmd.args.compressedSubImage.dy,
+ cmd.args.compressedSubImage.w, cmd.args.compressedSubImage.h,
+ cmd.args.compressedSubImage.glintformat,
+ cmd.args.compressedSubImage.size, cmd.args.compressedSubImage.data);
+ break;
+ case QGles2CommandBuffer::Command::BlitFromRenderbuffer:
+ {
+ GLuint fbo[2];
+ f->glGenFramebuffers(2, fbo);
+ f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo[0]);
+ f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER, cmd.args.blitFromRb.renderbuffer);
+ f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[1]);
+
+ f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromRb.target,
+ cmd.args.blitFromRb.texture, cmd.args.blitFromRb.dstLevel);
+ f->glBlitFramebuffer(0, 0, cmd.args.blitFromRb.w, cmd.args.blitFromRb.h,
+ 0, 0, cmd.args.blitFromRb.w, cmd.args.blitFromRb.h,
+ GL_COLOR_BUFFER_BIT,
+ GL_LINEAR);
+ f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
+ }
+ break;
+ case QGles2CommandBuffer::Command::GenMip:
+ f->glBindTexture(cmd.args.genMip.target, cmd.args.genMip.texture);
+ f->glGenerateMipmap(cmd.args.genMip.target);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void QRhiGles2::executeBindGraphicsPipeline(QRhiGraphicsPipeline *ps)
+{
+ QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, ps);
+
+ // No state tracking logic as of now. Could introduce something to reduce
+ // the number of gl* calls (when using and changing between multiple
+ // pipelines), but then begin/endExternal() should invalidate the cached
+ // state as appropriate.
+
+ if (psD->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor))
+ f->glEnable(GL_SCISSOR_TEST);
+ else
+ f->glDisable(GL_SCISSOR_TEST);
+ if (psD->m_cullMode == QRhiGraphicsPipeline::None) {
+ f->glDisable(GL_CULL_FACE);
+ } else {
+ f->glEnable(GL_CULL_FACE);
+ f->glCullFace(toGlCullMode(psD->m_cullMode));
+ }
+ f->glFrontFace(toGlFrontFace(psD->m_frontFace));
+ if (!psD->m_targetBlends.isEmpty()) {
+ const QRhiGraphicsPipeline::TargetBlend &blend(psD->m_targetBlends.first()); // no MRT
+ GLboolean wr = blend.colorWrite.testFlag(QRhiGraphicsPipeline::R);
+ GLboolean wg = blend.colorWrite.testFlag(QRhiGraphicsPipeline::G);
+ GLboolean wb = blend.colorWrite.testFlag(QRhiGraphicsPipeline::B);
+ GLboolean wa = blend.colorWrite.testFlag(QRhiGraphicsPipeline::A);
+ f->glColorMask(wr, wg, wb, wa);
+ if (blend.enable) {
+ f->glEnable(GL_BLEND);
+ f->glBlendFuncSeparate(toGlBlendFactor(blend.srcColor),
+ toGlBlendFactor(blend.dstColor),
+ toGlBlendFactor(blend.srcAlpha),
+ toGlBlendFactor(blend.dstAlpha));
+ f->glBlendEquationSeparate(toGlBlendOp(blend.opColor), toGlBlendOp(blend.opAlpha));
+ } else {
+ f->glDisable(GL_BLEND);
+ }
+ } else {
+ f->glDisable(GL_BLEND);
+ }
+ if (psD->m_depthTest)
+ f->glEnable(GL_DEPTH_TEST);
+ else
+ f->glDisable(GL_DEPTH_TEST);
+ if (psD->m_depthWrite)
+ f->glDepthMask(GL_TRUE);
+ else
+ f->glDepthMask(GL_FALSE);
+ f->glDepthFunc(toGlCompareOp(psD->m_depthOp));
+ if (psD->m_stencilTest) {
+ f->glEnable(GL_STENCIL_TEST);
+ f->glStencilFuncSeparate(GL_FRONT, toGlCompareOp(psD->m_stencilFront.compareOp), 0, psD->m_stencilReadMask);
+ f->glStencilOpSeparate(GL_FRONT,
+ toGlStencilOp(psD->m_stencilFront.failOp),
+ toGlStencilOp(psD->m_stencilFront.depthFailOp),
+ toGlStencilOp(psD->m_stencilFront.passOp));
+ f->glStencilMaskSeparate(GL_FRONT, psD->m_stencilWriteMask);
+ f->glStencilFuncSeparate(GL_BACK, toGlCompareOp(psD->m_stencilBack.compareOp), 0, psD->m_stencilReadMask);
+ f->glStencilOpSeparate(GL_BACK,
+ toGlStencilOp(psD->m_stencilBack.failOp),
+ toGlStencilOp(psD->m_stencilBack.depthFailOp),
+ toGlStencilOp(psD->m_stencilBack.passOp));
+ f->glStencilMaskSeparate(GL_BACK, psD->m_stencilWriteMask);
+ } else {
+ f->glDisable(GL_STENCIL_TEST);
+ }
+
+ f->glUseProgram(psD->program);
+}
+
+void QRhiGles2::bindShaderResources(QRhiGraphicsPipeline *ps, QRhiShaderResourceBindings *srb,
+ const uint *dynOfsPairs, int dynOfsCount)
+{
+ QGles2GraphicsPipeline *psD = QRHI_RES(QGles2GraphicsPipeline, ps);
+ QGles2ShaderResourceBindings *srbD = QRHI_RES(QGles2ShaderResourceBindings, srb);
+ int texUnit = 0;
+
+ for (int i = 0, ie = srbD->m_bindings.count(); i != ie; ++i) {
+ const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&srbD->m_bindings[i]);
+
+ switch (b->type) {
+ case QRhiShaderResourceBinding::UniformBuffer:
+ {
+ int viewOffset = b->u.ubuf.offset;
+ if (dynOfsCount) {
+ for (int j = 0; j < dynOfsCount; ++j) {
+ if (dynOfsPairs[2 * j] == uint(b->binding)) {
+ viewOffset = dynOfsPairs[2 * j + 1];
+ break;
+ }
+ }
+ }
+ QGles2Buffer *bufD = QRHI_RES(QGles2Buffer, b->u.ubuf.buf);
+ const QByteArray bufView = QByteArray::fromRawData(bufD->ubuf.constData() + viewOffset,
+ b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size);
+ for (QGles2GraphicsPipeline::Uniform &uniform : psD->uniforms) {
+ if (uniform.binding == b->binding) {
+ // in a uniform buffer everything is at least 4 byte aligned
+ // so this should not cause unaligned reads
+ const void *src = bufView.constData() + uniform.offset;
+
+ switch (uniform.type) {
+ case QShaderDescription::Float:
+ f->glUniform1f(uniform.glslLocation, *reinterpret_cast<const float *>(src));
+ break;
+ case QShaderDescription::Vec2:
+ f->glUniform2fv(uniform.glslLocation, 1, reinterpret_cast<const float *>(src));
+ break;
+ case QShaderDescription::Vec3:
+ f->glUniform3fv(uniform.glslLocation, 1, reinterpret_cast<const float *>(src));
+ break;
+ case QShaderDescription::Vec4:
+ f->glUniform4fv(uniform.glslLocation, 1, reinterpret_cast<const float *>(src));
+ break;
+ case QShaderDescription::Mat2:
+ f->glUniformMatrix2fv(uniform.glslLocation, 1, GL_FALSE, reinterpret_cast<const float *>(src));
+ break;
+ case QShaderDescription::Mat3:
+ f->glUniformMatrix3fv(uniform.glslLocation, 1, GL_FALSE, reinterpret_cast<const float *>(src));
+ break;
+ case QShaderDescription::Mat4:
+ f->glUniformMatrix4fv(uniform.glslLocation, 1, GL_FALSE, reinterpret_cast<const float *>(src));
+ break;
+ case QShaderDescription::Int:
+ f->glUniform1i(uniform.glslLocation, *reinterpret_cast<const qint32 *>(src));
+ break;
+ case QShaderDescription::Int2:
+ f->glUniform2iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
+ break;
+ case QShaderDescription::Int3:
+ f->glUniform3iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
+ break;
+ case QShaderDescription::Int4:
+ f->glUniform4iv(uniform.glslLocation, 1, reinterpret_cast<const qint32 *>(src));
+ break;
+ // ### more types
+ default:
+ break;
+ }
+ }
+ }
+ }
+ break;
+ case QRhiShaderResourceBinding::SampledTexture:
+ {
+ QGles2Texture *texD = QRHI_RES(QGles2Texture, b->u.stex.tex);
+ QGles2Sampler *samplerD = QRHI_RES(QGles2Sampler, b->u.stex.sampler);
+
+ for (QGles2GraphicsPipeline::Sampler &sampler : psD->samplers) {
+ if (sampler.binding == b->binding) {
+ f->glActiveTexture(GL_TEXTURE0 + texUnit);
+ f->glBindTexture(texD->target, texD->texture);
+
+ if (texD->samplerState != samplerD->d) {
+ f->glTexParameteri(texD->target, GL_TEXTURE_MIN_FILTER, samplerD->d.glminfilter);
+ f->glTexParameteri(texD->target, GL_TEXTURE_MAG_FILTER, samplerD->d.glmagfilter);
+ f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_S, samplerD->d.glwraps);
+ f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_T, samplerD->d.glwrapt);
+ // 3D textures not supported by GLES 2.0 or by us atm...
+ //f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_R, samplerD->d.glwrapr);
+ if (samplerD->d.gltexcomparefunc != GL_NEVER) {
+ f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
+ f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_FUNC, samplerD->d.gltexcomparefunc);
+ } else {
+ f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_NONE);
+ }
+ texD->samplerState = samplerD->d;
+ }
+
+ f->glUniform1i(sampler.glslLocation, texUnit);
+ ++texUnit;
+ }
+ }
+ }
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+
+ if (texUnit > 1)
+ f->glActiveTexture(GL_TEXTURE0);
+}
+
+void QRhiGles2::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_ASSERT(QRHI_RES(QGles2CommandBuffer, cb)->recordingPass == QGles2CommandBuffer::NoPass);
+
+ enqueueResourceUpdates(cb, resourceUpdates);
+}
+
+QGles2RenderTargetData *QRhiGles2::enqueueBindFramebuffer(QRhiRenderTarget *rt, QGles2CommandBuffer *cbD,
+ bool *wantsColorClear, bool *wantsDsClear)
+{
+ QGles2RenderTargetData *rtD = nullptr;
+ QGles2CommandBuffer::Command fbCmd;
+ fbCmd.cmd = QGles2CommandBuffer::Command::BindFramebuffer;
+ switch (rt->resourceType()) {
+ case QRhiResource::RenderTarget:
+ rtD = &QRHI_RES(QGles2ReferenceRenderTarget, rt)->d;
+ if (wantsColorClear)
+ *wantsColorClear = true;
+ if (wantsDsClear)
+ *wantsDsClear = true;
+ fbCmd.args.bindFramebuffer.fbo = 0;
+ fbCmd.args.bindFramebuffer.colorAttCount = 1;
+ break;
+ case QRhiResource::TextureRenderTarget:
+ {
+ QGles2TextureRenderTarget *rtTex = QRHI_RES(QGles2TextureRenderTarget, rt);
+ rtD = &rtTex->d;
+ if (wantsColorClear)
+ *wantsColorClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents);
+ if (wantsDsClear)
+ *wantsDsClear = !rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
+ fbCmd.args.bindFramebuffer.fbo = rtTex->framebuffer;
+ fbCmd.args.bindFramebuffer.colorAttCount = rtD->colorAttCount;
+ }
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ fbCmd.args.bindFramebuffer.srgb = rtD->srgbUpdateAndBlend;
+ cbD->commands.append(fbCmd);
+ return rtD;
+}
+
+void QRhiGles2::beginPass(QRhiCommandBuffer *cb,
+ QRhiRenderTarget *rt,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ QRhiResourceUpdateBatch *resourceUpdates)
+{
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::NoPass);
+
+ if (resourceUpdates)
+ enqueueResourceUpdates(cb, resourceUpdates);
+
+ bool wantsColorClear, wantsDsClear;
+ QGles2RenderTargetData *rtD = enqueueBindFramebuffer(rt, cbD, &wantsColorClear, &wantsDsClear);
+
+ QGles2CommandBuffer::Command clearCmd;
+ clearCmd.cmd = QGles2CommandBuffer::Command::Clear;
+ clearCmd.args.clear.mask = 0;
+ if (rtD->colorAttCount && wantsColorClear)
+ clearCmd.args.clear.mask |= GL_COLOR_BUFFER_BIT;
+ if (rtD->dsAttCount && wantsDsClear)
+ clearCmd.args.clear.mask |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
+ clearCmd.args.clear.c[0] = colorClearValue.redF();
+ clearCmd.args.clear.c[1] = colorClearValue.greenF();
+ clearCmd.args.clear.c[2] = colorClearValue.blueF();
+ clearCmd.args.clear.c[3] = colorClearValue.alphaF();
+ clearCmd.args.clear.d = depthStencilClearValue.depthClearValue();
+ clearCmd.args.clear.s = depthStencilClearValue.stencilClearValue();
+ cbD->commands.append(clearCmd);
+
+ cbD->recordingPass = QGles2CommandBuffer::RenderPass;
+ cbD->currentTarget = rt;
+}
+
+void QRhiGles2::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::RenderPass);
+
+ if (cbD->currentTarget->resourceType() == QRhiResource::TextureRenderTarget) {
+ QGles2TextureRenderTarget *rtTex = QRHI_RES(QGles2TextureRenderTarget, cbD->currentTarget);
+ const QVector<QRhiColorAttachment> colorAttachments = rtTex->m_desc.colorAttachments();
+ if (!colorAttachments.isEmpty()) {
+ // handle only 1 color attachment and only (msaa) renderbuffer
+ const QRhiColorAttachment &colorAtt(colorAttachments[0]);
+ if (colorAtt.resolveTexture()) {
+ Q_ASSERT(colorAtt.renderBuffer());
+ QGles2RenderBuffer *rbD = QRHI_RES(QGles2RenderBuffer, colorAtt.renderBuffer());
+ const QSize size = colorAtt.resolveTexture()->pixelSize();
+ if (rbD->pixelSize() != size) {
+ qWarning("Resolve source (%dx%d) and target (%dx%d) size does not match",
+ rbD->pixelSize().width(), rbD->pixelSize().height(), size.width(), size.height());
+ }
+ QGles2CommandBuffer::Command cmd;
+ cmd.cmd = QGles2CommandBuffer::Command::BlitFromRenderbuffer;
+ cmd.args.blitFromRb.renderbuffer = rbD->renderbuffer;
+ cmd.args.blitFromRb.w = size.width();
+ cmd.args.blitFromRb.h = size.height();
+ QGles2Texture *colorTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture());
+ const GLenum faceTargetBase = colorTexD->m_flags.testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X
+ : colorTexD->target;
+ cmd.args.blitFromRb.target = faceTargetBase + colorAtt.resolveLayer();
+ cmd.args.blitFromRb.texture = colorTexD->texture;
+ cmd.args.blitFromRb.dstLevel = colorAtt.resolveLevel();
+ cbD->commands.append(cmd);
+ }
+ }
+ }
+
+ cbD->recordingPass = QGles2CommandBuffer::NoPass;
+ cbD->currentTarget = nullptr;
+
+ if (resourceUpdates)
+ enqueueResourceUpdates(cb, resourceUpdates);
+}
+
+void QRhiGles2::beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::NoPass);
+
+ if (resourceUpdates)
+ enqueueResourceUpdates(cb, resourceUpdates);
+
+ cbD->recordingPass = QGles2CommandBuffer::ComputePass;
+}
+
+void QRhiGles2::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QGles2CommandBuffer::ComputePass);
+
+ cbD->recordingPass = QGles2CommandBuffer::NoPass;
+
+ if (resourceUpdates)
+ enqueueResourceUpdates(cb, resourceUpdates);
+}
+
+void QRhiGles2::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps)
+{
+ Q_UNUSED(cb);
+ Q_UNUSED(ps);
+}
+
+void QRhiGles2::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
+{
+ Q_UNUSED(cb);
+ Q_UNUSED(x);
+ Q_UNUSED(y);
+ Q_UNUSED(z);
+}
+
+QGles2Buffer::QGles2Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size)
+ : QRhiBuffer(rhi, type, usage, size)
+{
+}
+
+QGles2Buffer::~QGles2Buffer()
+{
+ release();
+}
+
+void QGles2Buffer::release()
+{
+ if (!buffer)
+ return;
+
+ QRhiGles2::DeferredReleaseEntry e;
+ e.type = QRhiGles2::DeferredReleaseEntry::Buffer;
+
+ e.buffer.buffer = buffer;
+
+ buffer = 0;
+
+ QRHI_RES_RHI(QRhiGles2);
+ rhiD->releaseQueue.append(e);
+ QRHI_PROF;
+ QRHI_PROF_F(releaseBuffer(this));
+ rhiD->unregisterResource(this);
+}
+
+bool QGles2Buffer::build()
+{
+ if (buffer)
+ release();
+
+ QRHI_RES_RHI(QRhiGles2);
+ QRHI_PROF;
+
+ if (m_usage.testFlag(QRhiBuffer::UniformBuffer)) {
+ if (m_usage.testFlag(QRhiBuffer::VertexBuffer) || m_usage.testFlag(QRhiBuffer::IndexBuffer)) {
+ qWarning("Uniform buffer: multiple usages specified, this is not supported by the OpenGL backend");
+ return false;
+ }
+ ubuf.resize(m_size);
+ QRHI_PROF_F(newBuffer(this, m_size, 0, 1));
+ return true;
+ }
+
+ if (!rhiD->ensureContext())
+ return false;
+
+ if (m_usage.testFlag(QRhiBuffer::VertexBuffer)) {
+ if (m_usage.testFlag(QRhiBuffer::IndexBuffer)) {
+ qWarning("Vertex buffer: multiple usages specified, this is not supported by the OpenGL backend");
+ return false;
+ }
+ target = GL_ARRAY_BUFFER;
+ }
+ if (m_usage.testFlag(QRhiBuffer::IndexBuffer))
+ target = GL_ELEMENT_ARRAY_BUFFER;
+
+ rhiD->f->glGenBuffers(1, &buffer);
+ rhiD->f->glBindBuffer(target, buffer);
+ rhiD->f->glBufferData(target, m_size, nullptr, m_type == Dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
+
+ QRHI_PROF_F(newBuffer(this, m_size, 1, 0));
+ rhiD->registerResource(this);
+ return true;
+}
+
+QGles2RenderBuffer::QGles2RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags)
+ : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags)
+{
+}
+
+QGles2RenderBuffer::~QGles2RenderBuffer()
+{
+ release();
+}
+
+void QGles2RenderBuffer::release()
+{
+ if (!renderbuffer)
+ return;
+
+ QRhiGles2::DeferredReleaseEntry e;
+ e.type = QRhiGles2::DeferredReleaseEntry::RenderBuffer;
+
+ e.renderbuffer.renderbuffer = renderbuffer;
+ e.renderbuffer.renderbuffer2 = stencilRenderbuffer;
+
+ renderbuffer = 0;
+ stencilRenderbuffer = 0;
+
+ QRHI_RES_RHI(QRhiGles2);
+ rhiD->releaseQueue.append(e);
+ QRHI_PROF;
+ QRHI_PROF_F(releaseRenderBuffer(this));
+ rhiD->unregisterResource(this);
+}
+
+bool QGles2RenderBuffer::build()
+{
+ if (renderbuffer)
+ release();
+
+ QRHI_RES_RHI(QRhiGles2);
+ QRHI_PROF;
+ samples = rhiD->effectiveSampleCount(m_sampleCount);
+
+ if (m_flags.testFlag(UsedWithSwapChainOnly)) {
+ if (m_type == DepthStencil) {
+ QRHI_PROF_F(newRenderBuffer(this, false, true, samples));
+ return true;
+ }
+
+ qWarning("RenderBuffer: UsedWithSwapChainOnly is meaningless in combination with Color");
+ }
+
+ if (m_pixelSize.isEmpty())
+ return false;
+
+ if (!rhiD->ensureContext())
+ return false;
+
+ rhiD->f->glGenRenderbuffers(1, &renderbuffer);
+ rhiD->f->glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
+
+ switch (m_type) {
+ case QRhiRenderBuffer::DepthStencil:
+ if (rhiD->caps.msaaRenderBuffer && samples > 1) {
+ rhiD->f->glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_DEPTH24_STENCIL8,
+ m_pixelSize.width(), m_pixelSize.height());
+ } else {
+ if (rhiD->caps.packedDepthStencil) {
+ rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8,
+ m_pixelSize.width(), m_pixelSize.height());
+ stencilRenderbuffer = 0;
+ } else {
+ rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_ATTACHMENT,
+ m_pixelSize.width(), m_pixelSize.height());
+ rhiD->f->glGenRenderbuffers(1, &stencilRenderbuffer);
+ rhiD->f->glBindRenderbuffer(GL_RENDERBUFFER, stencilRenderbuffer);
+ rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_ATTACHMENT,
+ m_pixelSize.width(), m_pixelSize.height());
+ }
+ }
+ QRHI_PROF_F(newRenderBuffer(this, false, false, samples));
+ break;
+ case QRhiRenderBuffer::Color:
+ if (rhiD->caps.msaaRenderBuffer && samples > 1)
+ rhiD->f->glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_RGBA8,
+ m_pixelSize.width(), m_pixelSize.height());
+ else
+ rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8,
+ m_pixelSize.width(), m_pixelSize.height());
+ QRHI_PROF_F(newRenderBuffer(this, false, false, samples));
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ rhiD->registerResource(this);
+ return true;
+}
+
+QRhiTexture::Format QGles2RenderBuffer::backingFormat() const
+{
+ return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
+}
+
+QGles2Texture::QGles2Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+ int sampleCount, Flags flags)
+ : QRhiTexture(rhi, format, pixelSize, sampleCount, flags)
+{
+}
+
+QGles2Texture::~QGles2Texture()
+{
+ release();
+}
+
+void QGles2Texture::release()
+{
+ if (!texture)
+ return;
+
+ QRhiGles2::DeferredReleaseEntry e;
+ e.type = QRhiGles2::DeferredReleaseEntry::Texture;
+
+ e.texture.texture = texture;
+
+ texture = 0;
+ specified = false;
+ nativeHandlesStruct.texture = 0;
+
+ QRHI_RES_RHI(QRhiGles2);
+ if (owns)
+ rhiD->releaseQueue.append(e);
+ QRHI_PROF;
+ QRHI_PROF_F(releaseTexture(this));
+ rhiD->unregisterResource(this);
+}
+
+static inline bool isPowerOfTwo(int x)
+{
+ // Assumption: x >= 1
+ return x == (x & -x);
+}
+
+bool QGles2Texture::prepareBuild(QSize *adjustedSize)
+{
+ if (texture)
+ release();
+
+ QRHI_RES_RHI(QRhiGles2);
+ if (!rhiD->ensureContext())
+ return false;
+
+ QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
+ if (!rhiD->caps.npotTexture && (!isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height())))
+ size = QSize(qNextPowerOfTwo(size.width()), qNextPowerOfTwo(size.height()));
+
+ const bool isCube = m_flags.testFlag(CubeMap);
+ const bool hasMipMaps = m_flags.testFlag(MipMapped);
+ const bool isCompressed = rhiD->isCompressedFormat(m_format);
+
+ target = isCube ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D;
+ mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
+ gltype = GL_UNSIGNED_BYTE;
+
+ if (isCompressed) {
+ glintformat = toGlCompressedTextureFormat(m_format, m_flags);
+ if (!glintformat) {
+ qWarning("Compressed format %d not mappable to GL compressed format", m_format);
+ return false;
+ }
+ glformat = GL_RGBA;
+ } else {
+ switch (m_format) {
+ case QRhiTexture::RGBA8:
+ glintformat = GL_RGBA;
+ glformat = GL_RGBA;
+ break;
+ case QRhiTexture::BGRA8:
+ glintformat = rhiD->caps.bgraInternalFormat ? GL_BGRA : GL_RGBA;
+ glformat = GL_BGRA;
+ break;
+ case QRhiTexture::R16:
+ glintformat = GL_R16;
+ glformat = GL_RED;
+ gltype = GL_UNSIGNED_SHORT;
+ break;
+ case QRhiTexture::R8:
+ glintformat = GL_R8;
+ glformat = GL_RED;
+ break;
+ case QRhiTexture::RED_OR_ALPHA8:
+ glintformat = rhiD->caps.coreProfile ? GL_R8 : GL_ALPHA;
+ glformat = rhiD->caps.coreProfile ? GL_RED : GL_ALPHA;
+ break;
+ case QRhiTexture::RGBA16F:
+ glintformat = GL_RGBA16F;
+ glformat = GL_RGBA;
+ gltype = GL_HALF_FLOAT;
+ break;
+ case QRhiTexture::RGBA32F:
+ glintformat = GL_RGBA32F;
+ glformat = GL_RGBA;
+ gltype = GL_FLOAT;
+ break;
+ case QRhiTexture::D16:
+ glintformat = GL_DEPTH_COMPONENT16;
+ glformat = GL_DEPTH_COMPONENT;
+ gltype = GL_UNSIGNED_SHORT;
+ break;
+ case QRhiTexture::D32F:
+ glintformat = GL_DEPTH_COMPONENT32F;
+ glformat = GL_DEPTH_COMPONENT;
+ gltype = GL_FLOAT;
+ break;
+ default:
+ Q_UNREACHABLE();
+ glintformat = GL_RGBA;
+ glformat = GL_RGBA;
+ break;
+ }
+ }
+
+ samplerState = QGles2SamplerData();
+
+ if (adjustedSize)
+ *adjustedSize = size;
+
+ return true;
+}
+
+bool QGles2Texture::build()
+{
+ QSize size;
+ if (!prepareBuild(&size))
+ return false;
+
+ QRHI_RES_RHI(QRhiGles2);
+ rhiD->f->glGenTextures(1, &texture);
+
+ const bool isCube = m_flags.testFlag(CubeMap);
+ const bool hasMipMaps = m_flags.testFlag(MipMapped);
+ const bool isCompressed = rhiD->isCompressedFormat(m_format);
+ if (!isCompressed) {
+ rhiD->f->glBindTexture(target, texture);
+ if (hasMipMaps || isCube) {
+ const GLenum faceTargetBase = isCube ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : target;
+ for (int layer = 0, layerCount = isCube ? 6 : 1; layer != layerCount; ++layer) {
+ for (int level = 0; level != mipLevelCount; ++level) {
+ const QSize mipSize = rhiD->q->sizeForMipLevel(level, size);
+ rhiD->f->glTexImage2D(faceTargetBase + layer, level, glintformat,
+ mipSize.width(), mipSize.height(), 0,
+ glformat, gltype, nullptr);
+ }
+ }
+ } else {
+ rhiD->f->glTexImage2D(target, 0, glintformat, size.width(), size.height(), 0, glformat, gltype, nullptr);
+ }
+ specified = true;
+ } else {
+ // Cannot use glCompressedTexImage2D without valid data, so defer.
+ // Compressed textures will not be used as render targets so this is
+ // not an issue.
+ specified = false;
+ }
+
+ QRHI_PROF;
+ QRHI_PROF_F(newTexture(this, true, mipLevelCount, isCube ? 6 : 1, 1));
+
+ owns = true;
+ nativeHandlesStruct.texture = texture;
+
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+bool QGles2Texture::buildFrom(const QRhiNativeHandles *src)
+{
+ const QRhiGles2TextureNativeHandles *h = static_cast<const QRhiGles2TextureNativeHandles *>(src);
+ if (!h || !h->texture)
+ return false;
+
+ if (!prepareBuild())
+ return false;
+
+ texture = h->texture;
+ specified = true;
+
+ QRHI_RES_RHI(QRhiGles2);
+ QRHI_PROF;
+ QRHI_PROF_F(newTexture(this, false, mipLevelCount, m_flags.testFlag(CubeMap) ? 6 : 1, 1));
+
+ owns = false;
+ nativeHandlesStruct.texture = texture;
+
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+const QRhiNativeHandles *QGles2Texture::nativeHandles()
+{
+ return &nativeHandlesStruct;
+}
+
+QGles2Sampler::QGles2Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
+ AddressMode u, AddressMode v)
+ : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v)
+{
+}
+
+QGles2Sampler::~QGles2Sampler()
+{
+ release();
+}
+
+void QGles2Sampler::release()
+{
+ // nothing to do here
+}
+
+bool QGles2Sampler::build()
+{
+ d.glminfilter = toGlMinFilter(m_minFilter, m_mipmapMode);
+ d.glmagfilter = toGlMagFilter(m_magFilter);
+ d.glwraps = toGlWrapMode(m_addressU);
+ d.glwrapt = toGlWrapMode(m_addressV);
+ d.glwrapr = toGlWrapMode(m_addressW);
+ d.gltexcomparefunc = toGlTextureCompareFunc(m_compareOp);
+
+ generation += 1;
+ return true;
+}
+
+// dummy, no Vulkan-style RenderPass+Framebuffer concept here
+QGles2RenderPassDescriptor::QGles2RenderPassDescriptor(QRhiImplementation *rhi)
+ : QRhiRenderPassDescriptor(rhi)
+{
+}
+
+QGles2RenderPassDescriptor::~QGles2RenderPassDescriptor()
+{
+ release();
+}
+
+void QGles2RenderPassDescriptor::release()
+{
+ // nothing to do here
+}
+
+QGles2ReferenceRenderTarget::QGles2ReferenceRenderTarget(QRhiImplementation *rhi)
+ : QRhiRenderTarget(rhi),
+ d(rhi)
+{
+}
+
+QGles2ReferenceRenderTarget::~QGles2ReferenceRenderTarget()
+{
+ release();
+}
+
+void QGles2ReferenceRenderTarget::release()
+{
+ // nothing to do here
+}
+
+QSize QGles2ReferenceRenderTarget::pixelSize() const
+{
+ return d.pixelSize;
+}
+
+float QGles2ReferenceRenderTarget::devicePixelRatio() const
+{
+ return d.dpr;
+}
+
+int QGles2ReferenceRenderTarget::sampleCount() const
+{
+ return d.sampleCount;
+}
+
+QGles2TextureRenderTarget::QGles2TextureRenderTarget(QRhiImplementation *rhi,
+ const QRhiTextureRenderTargetDescription &desc,
+ Flags flags)
+ : QRhiTextureRenderTarget(rhi, desc, flags),
+ d(rhi)
+{
+}
+
+QGles2TextureRenderTarget::~QGles2TextureRenderTarget()
+{
+ release();
+}
+
+void QGles2TextureRenderTarget::release()
+{
+ if (!framebuffer)
+ return;
+
+ QRhiGles2::DeferredReleaseEntry e;
+ e.type = QRhiGles2::DeferredReleaseEntry::TextureRenderTarget;
+
+ e.textureRenderTarget.framebuffer = framebuffer;
+
+ framebuffer = 0;
+
+ QRHI_RES_RHI(QRhiGles2);
+ rhiD->releaseQueue.append(e);
+
+ rhiD->unregisterResource(this);
+}
+
+QRhiRenderPassDescriptor *QGles2TextureRenderTarget::newCompatibleRenderPassDescriptor()
+{
+ return new QGles2RenderPassDescriptor(m_rhi);
+}
+
+bool QGles2TextureRenderTarget::build()
+{
+ QRHI_RES_RHI(QRhiGles2);
+
+ if (framebuffer)
+ release();
+
+ const QVector<QRhiColorAttachment> colorAttachments = m_desc.colorAttachments();
+ Q_ASSERT(!colorAttachments.isEmpty() || m_desc.depthTexture());
+ Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
+ const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
+
+ if (colorAttachments.count() > rhiD->caps.maxDrawBuffers)
+ qWarning("QGles2TextureRenderTarget: Too many color attachments (%d, max is %d)",
+ colorAttachments.count(), rhiD->caps.maxDrawBuffers);
+ if (m_desc.depthTexture() && !rhiD->caps.depthTexture)
+ qWarning("QGles2TextureRenderTarget: Depth texture is not supported and will be ignored");
+
+ if (!rhiD->ensureContext())
+ return false;
+
+ rhiD->f->glGenFramebuffers(1, &framebuffer);
+ rhiD->f->glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
+
+ d.colorAttCount = colorAttachments.count();
+ for (int i = 0; i < d.colorAttCount; ++i) {
+ const QRhiColorAttachment &colorAtt(colorAttachments[i]);
+ QRhiTexture *texture = colorAtt.texture();
+ QRhiRenderBuffer *renderBuffer = colorAtt.renderBuffer();
+ Q_ASSERT(texture || renderBuffer);
+ if (texture) {
+ QGles2Texture *texD = QRHI_RES(QGles2Texture, texture);
+ Q_ASSERT(texD->texture && texD->specified);
+ const GLenum faceTargetBase = texD->flags().testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : texD->target;
+ rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, faceTargetBase + colorAtt.layer(), texD->texture, colorAtt.level());
+ if (i == 0) {
+ d.pixelSize = texD->pixelSize();
+ d.sampleCount = 1;
+ }
+ } else if (renderBuffer) {
+ QGles2RenderBuffer *rbD = QRHI_RES(QGles2RenderBuffer, renderBuffer);
+ rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_RENDERBUFFER, rbD->renderbuffer);
+ if (i == 0) {
+ d.pixelSize = rbD->pixelSize();
+ d.sampleCount = rbD->samples;
+ }
+ }
+ }
+
+ if (hasDepthStencil) {
+ if (m_desc.depthStencilBuffer()) {
+ QGles2RenderBuffer *depthRbD = QRHI_RES(QGles2RenderBuffer, m_desc.depthStencilBuffer());
+ rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRbD->renderbuffer);
+ if (depthRbD->stencilRenderbuffer)
+ rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthRbD->stencilRenderbuffer);
+ else // packed
+ rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthRbD->renderbuffer);
+ if (d.colorAttCount == 0) {
+ d.pixelSize = depthRbD->pixelSize();
+ d.sampleCount = depthRbD->samples;
+ }
+ } else {
+ QGles2Texture *depthTexD = QRHI_RES(QGles2Texture, m_desc.depthTexture());
+ rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexD->texture, 0);
+ if (d.colorAttCount == 0) {
+ d.pixelSize = depthTexD->pixelSize();
+ d.sampleCount = 1;
+ }
+ }
+ d.dsAttCount = 1;
+ } else {
+ d.dsAttCount = 0;
+ }
+
+ d.dpr = 1;
+ d.rp = QRHI_RES(QGles2RenderPassDescriptor, m_renderPassDesc);
+
+ GLenum status = rhiD->f->glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status != GL_NO_ERROR && status != GL_FRAMEBUFFER_COMPLETE) {
+ qWarning("Framebuffer incomplete: 0x%x", status);
+ return false;
+ }
+
+ rhiD->registerResource(this);
+ return true;
+}
+
+QSize QGles2TextureRenderTarget::pixelSize() const
+{
+ return d.pixelSize;
+}
+
+float QGles2TextureRenderTarget::devicePixelRatio() const
+{
+ return d.dpr;
+}
+
+int QGles2TextureRenderTarget::sampleCount() const
+{
+ return d.sampleCount;
+}
+
+QGles2ShaderResourceBindings::QGles2ShaderResourceBindings(QRhiImplementation *rhi)
+ : QRhiShaderResourceBindings(rhi)
+{
+}
+
+QGles2ShaderResourceBindings::~QGles2ShaderResourceBindings()
+{
+ release();
+}
+
+void QGles2ShaderResourceBindings::release()
+{
+ // nothing to do here
+}
+
+bool QGles2ShaderResourceBindings::build()
+{
+ generation += 1;
+ return true;
+}
+
+QGles2GraphicsPipeline::QGles2GraphicsPipeline(QRhiImplementation *rhi)
+ : QRhiGraphicsPipeline(rhi)
+{
+}
+
+QGles2GraphicsPipeline::~QGles2GraphicsPipeline()
+{
+ release();
+}
+
+void QGles2GraphicsPipeline::release()
+{
+ if (!program)
+ return;
+
+ QRhiGles2::DeferredReleaseEntry e;
+ e.type = QRhiGles2::DeferredReleaseEntry::Pipeline;
+
+ e.pipeline.program = program;
+
+ program = 0;
+ uniforms.clear();
+ samplers.clear();
+
+ QRHI_RES_RHI(QRhiGles2);
+ rhiD->releaseQueue.append(e);
+
+ rhiD->unregisterResource(this);
+}
+
+bool QGles2GraphicsPipeline::build()
+{
+ QRHI_RES_RHI(QRhiGles2);
+
+ if (program)
+ release();
+
+ if (!rhiD->ensureContext())
+ return false;
+
+ drawMode = toGlTopology(m_topology);
+
+ program = rhiD->f->glCreateProgram();
+
+ int sourceVer = 0;
+ for (const QRhiShaderStage &shaderStage : qAsConst(m_shaderStages)) {
+ const bool isVertex = shaderStage.type() == QRhiShaderStage::Vertex;
+ const bool isFragment = shaderStage.type() == QRhiShaderStage::Fragment;
+ if (!isVertex && !isFragment)
+ continue;
+
+ GLuint shader = rhiD->f->glCreateShader(isVertex ? GL_VERTEX_SHADER : GL_FRAGMENT_SHADER);
+ const QShader bakedShader = shaderStage.shader();
+ QVector<int> versionsToTry;
+ QByteArray source;
+ if (rhiD->caps.gles) {
+ if (rhiD->caps.ctxMajor > 3 || (rhiD->caps.ctxMajor == 3 && rhiD->caps.ctxMinor >= 2)) {
+ versionsToTry << 320 << 310 << 300 << 100;
+ } else if (rhiD->caps.ctxMajor == 3 && rhiD->caps.ctxMinor == 1) {
+ versionsToTry << 310 << 300 << 100;
+ } else if (rhiD->caps.ctxMajor == 3 && rhiD->caps.ctxMinor == 0) {
+ versionsToTry << 300 << 100;
+ } else {
+ versionsToTry << 100;
+ }
+ for (int v : versionsToTry) {
+ QShaderVersion ver(v, QShaderVersion::GlslEs);
+ source = bakedShader.shader({ QShader::GlslShader, ver, shaderStage.shaderVariant() }).shader();
+ if (!source.isEmpty()) {
+ sourceVer = v;
+ break;
+ }
+ }
+ } else {
+ if (rhiD->caps.ctxMajor > 4 || (rhiD->caps.ctxMajor == 4 && rhiD->caps.ctxMinor >= 6)) {
+ versionsToTry << 460 << 450 << 440 << 430 << 420 << 410 << 400 << 330 << 150;
+ } else if (rhiD->caps.ctxMajor == 4 && rhiD->caps.ctxMinor == 5) {
+ versionsToTry << 450 << 440 << 430 << 420 << 410 << 400 << 330 << 150;
+ } else if (rhiD->caps.ctxMajor == 4 && rhiD->caps.ctxMinor == 4) {
+ versionsToTry << 440 << 430 << 420 << 410 << 400 << 330 << 150;
+ } else if (rhiD->caps.ctxMajor == 4 && rhiD->caps.ctxMinor == 3) {
+ versionsToTry << 430 << 420 << 410 << 400 << 330 << 150;
+ } else if (rhiD->caps.ctxMajor == 4 && rhiD->caps.ctxMinor == 2) {
+ versionsToTry << 420 << 410 << 400 << 330 << 150;
+ } else if (rhiD->caps.ctxMajor == 4 && rhiD->caps.ctxMinor == 1) {
+ versionsToTry << 410 << 400 << 330 << 150;
+ } else if (rhiD->caps.ctxMajor == 4 && rhiD->caps.ctxMinor == 0) {
+ versionsToTry << 400 << 330 << 150;
+ } else if (rhiD->caps.ctxMajor == 3 && rhiD->caps.ctxMinor == 3) {
+ versionsToTry << 330 << 150;
+ } else if (rhiD->caps.ctxMajor == 3 && rhiD->caps.ctxMinor == 2) {
+ versionsToTry << 150;
+ }
+ if (!rhiD->caps.coreProfile)
+ versionsToTry << 120;
+ for (int v : versionsToTry) {
+ source = bakedShader.shader({ QShader::GlslShader, v, shaderStage.shaderVariant() }).shader();
+ if (!source.isEmpty()) {
+ sourceVer = v;
+ break;
+ }
+ }
+ }
+ if (source.isEmpty()) {
+ qWarning() << "No GLSL shader code found (versions tried: " << versionsToTry
+ << ") in baked shader" << bakedShader;
+ return false;
+ }
+
+ const char *srcStr = source.constData();
+ const GLint srcLength = source.count();
+ rhiD->f->glShaderSource(shader, 1, &srcStr, &srcLength);
+ rhiD->f->glCompileShader(shader);
+ GLint compiled = 0;
+ rhiD->f->glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+ if (!compiled) {
+ GLint infoLogLength = 0;
+ rhiD->f->glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
+ QByteArray log;
+ if (infoLogLength > 1) {
+ GLsizei length = 0;
+ log.resize(infoLogLength);
+ rhiD->f->glGetShaderInfoLog(shader, infoLogLength, &length, log.data());
+ }
+ qWarning("Failed to compile shader: %s\nSource was:\n%s", log.constData(), source.constData());
+ return false;
+ }
+
+ rhiD->f->glAttachShader(program, shader);
+ rhiD->f->glDeleteShader(shader);
+
+ if (isVertex)
+ vsDesc = bakedShader.description();
+ else
+ fsDesc = bakedShader.description();
+ }
+
+ // not very useful atm since we assume the qsb-generated GLSL shaders never use uniform blocks
+ canUseUniformBuffers = rhiD->caps.uniformBuffers && sourceVer >= 140;
+
+ for (auto inVar : vsDesc.inputVariables()) {
+ const QByteArray name = inVar.name.toUtf8();
+ rhiD->f->glBindAttribLocation(program, inVar.location, name.constData());
+ }
+
+ rhiD->f->glLinkProgram(program);
+ GLint linked = 0;
+ rhiD->f->glGetProgramiv(program, GL_LINK_STATUS, &linked);
+ if (!linked) {
+ GLint infoLogLength = 0;
+ rhiD->f->glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);
+ QByteArray log;
+ if (infoLogLength > 1) {
+ GLsizei length = 0;
+ log.resize(infoLogLength);
+ rhiD->f->glGetProgramInfoLog(program, infoLogLength, &length, log.data());
+ }
+ qWarning("Failed to link shader program: %s", log.constData());
+ return false;
+ }
+
+ auto lookupUniforms = [this, rhiD](const QShaderDescription::UniformBlock &ub) {
+ const QByteArray prefix = ub.structName.toUtf8() + '.';
+ for (const QShaderDescription::BlockVariable &blockMember : ub.members) {
+ // ### no array support for now
+ Uniform uniform;
+ uniform.type = blockMember.type;
+ const QByteArray name = prefix + blockMember.name.toUtf8();
+ uniform.glslLocation = rhiD->f->glGetUniformLocation(program, name.constData());
+ if (uniform.glslLocation >= 0) {
+ uniform.binding = ub.binding;
+ uniform.offset = blockMember.offset;
+ uniform.size = blockMember.size;
+ uniforms.append(uniform);
+ }
+ }
+ };
+
+ for (const QShaderDescription::UniformBlock &ub : vsDesc.uniformBlocks())
+ lookupUniforms(ub);
+
+ for (const QShaderDescription::UniformBlock &ub : fsDesc.uniformBlocks())
+ lookupUniforms(ub);
+
+ auto lookupSamplers = [this, rhiD](const QShaderDescription::InOutVariable &v) {
+ Sampler sampler;
+ const QByteArray name = v.name.toUtf8();
+ sampler.glslLocation = rhiD->f->glGetUniformLocation(program, name.constData());
+ if (sampler.glslLocation >= 0) {
+ sampler.binding = v.binding;
+ samplers.append(sampler);
+ }
+ };
+
+ for (const QShaderDescription::InOutVariable &v : vsDesc.combinedImageSamplers())
+ lookupSamplers(v);
+
+ for (const QShaderDescription::InOutVariable &v : fsDesc.combinedImageSamplers())
+ lookupSamplers(v);
+
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+QGles2ComputePipeline::QGles2ComputePipeline(QRhiImplementation *rhi)
+ : QRhiComputePipeline(rhi)
+{
+}
+
+QGles2ComputePipeline::~QGles2ComputePipeline()
+{
+ release();
+}
+
+void QGles2ComputePipeline::release()
+{
+}
+
+bool QGles2ComputePipeline::build()
+{
+ return false;
+}
+
+QGles2CommandBuffer::QGles2CommandBuffer(QRhiImplementation *rhi)
+ : QRhiCommandBuffer(rhi)
+{
+ resetState();
+}
+
+QGles2CommandBuffer::~QGles2CommandBuffer()
+{
+ release();
+}
+
+void QGles2CommandBuffer::release()
+{
+ // nothing to do here
+}
+
+QGles2SwapChain::QGles2SwapChain(QRhiImplementation *rhi)
+ : QRhiSwapChain(rhi),
+ rt(rhi),
+ cb(rhi)
+{
+}
+
+QGles2SwapChain::~QGles2SwapChain()
+{
+ release();
+}
+
+void QGles2SwapChain::release()
+{
+ QRHI_PROF;
+ QRHI_PROF_F(releaseSwapChain(this));
+}
+
+QRhiCommandBuffer *QGles2SwapChain::currentFrameCommandBuffer()
+{
+ return &cb;
+}
+
+QRhiRenderTarget *QGles2SwapChain::currentFrameRenderTarget()
+{
+ return &rt;
+}
+
+QSize QGles2SwapChain::surfacePixelSize()
+{
+ Q_ASSERT(m_window);
+ return m_window->size() * m_window->devicePixelRatio();
+}
+
+QRhiRenderPassDescriptor *QGles2SwapChain::newCompatibleRenderPassDescriptor()
+{
+ return new QGles2RenderPassDescriptor(m_rhi);
+}
+
+bool QGles2SwapChain::buildOrResize()
+{
+ surface = m_window;
+ m_currentPixelSize = surfacePixelSize();
+ pixelSize = m_currentPixelSize;
+
+ rt.d.rp = QRHI_RES(QGles2RenderPassDescriptor, m_renderPassDesc);
+ rt.d.pixelSize = pixelSize;
+ rt.d.dpr = m_window->devicePixelRatio();
+ rt.d.sampleCount = qBound(1, m_sampleCount, 64);
+ rt.d.colorAttCount = 1;
+ rt.d.dsAttCount = m_depthStencil ? 1 : 0;
+ rt.d.srgbUpdateAndBlend = m_flags.testFlag(QRhiSwapChain::sRGB);
+
+ frameCount = 0;
+
+ QRHI_PROF;
+ // make something up
+ QRHI_PROF_F(resizeSwapChain(this, 2, m_sampleCount > 1 ? 2 : 0, m_sampleCount));
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/rhi/qrhigles2_p.h b/src/gui/rhi/qrhigles2_p.h
new file mode 100644
index 0000000000..7f7c8b4c40
--- /dev/null
+++ b/src/gui/rhi/qrhigles2_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHIGLES2_H
+#define QRHIGLES2_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qrhi_p.h>
+#include <QtGui/qsurfaceformat.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOpenGLContext;
+class QOffscreenSurface;
+class QWindow;
+
+struct Q_GUI_EXPORT QRhiGles2InitParams : public QRhiInitParams
+{
+ QRhiGles2InitParams();
+
+ QSurfaceFormat format;
+ QOffscreenSurface *fallbackSurface = nullptr;
+ QWindow *window = nullptr;
+
+ static QOffscreenSurface *newFallbackSurface(const QSurfaceFormat &format = QSurfaceFormat::defaultFormat());
+ static QSurfaceFormat adjustedFormat(const QSurfaceFormat &format = QSurfaceFormat::defaultFormat());
+};
+
+struct Q_GUI_EXPORT QRhiGles2NativeHandles : public QRhiNativeHandles
+{
+ QOpenGLContext *context = nullptr;
+};
+
+struct Q_GUI_EXPORT QRhiGles2TextureNativeHandles : public QRhiNativeHandles
+{
+ uint texture = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h
new file mode 100644
index 0000000000..fe74e2e75b
--- /dev/null
+++ b/src/gui/rhi/qrhigles2_p_p.h
@@ -0,0 +1,716 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHIGLES2_P_H
+#define QRHIGLES2_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qrhigles2_p.h"
+#include "qrhi_p_p.h"
+#include "qshaderdescription_p.h"
+#include <qopengl.h>
+#include <QSurface>
+
+QT_BEGIN_NAMESPACE
+
+class QOpenGLExtensions;
+
+struct QGles2Buffer : public QRhiBuffer
+{
+ QGles2Buffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size);
+ ~QGles2Buffer();
+ void release() override;
+ bool build() override;
+
+ GLuint buffer = 0;
+ GLenum target;
+ QByteArray ubuf;
+ friend class QRhiGles2;
+};
+
+struct QGles2RenderBuffer : public QRhiRenderBuffer
+{
+ QGles2RenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags);
+ ~QGles2RenderBuffer();
+ void release() override;
+ bool build() override;
+ QRhiTexture::Format backingFormat() const override;
+
+ GLuint renderbuffer = 0;
+ GLuint stencilRenderbuffer = 0; // when packed depth-stencil not supported
+ int samples;
+ friend class QRhiGles2;
+};
+
+struct QGles2SamplerData
+{
+ GLenum glminfilter = 0;
+ GLenum glmagfilter = 0;
+ GLenum glwraps = 0;
+ GLenum glwrapt = 0;
+ GLenum glwrapr = 0;
+ GLenum gltexcomparefunc = 0;
+};
+
+inline bool operator==(const QGles2SamplerData &a, const QGles2SamplerData &b)
+{
+ return a.glminfilter == b.glminfilter
+ && a.glmagfilter == b.glmagfilter
+ && a.glwraps == b.glwraps
+ && a.glwrapt == b.glwrapt
+ && a.glwrapr == b.glwrapr
+ && a.gltexcomparefunc == b.gltexcomparefunc;
+}
+
+inline bool operator!=(const QGles2SamplerData &a, const QGles2SamplerData &b)
+{
+ return !(a == b);
+}
+
+struct QGles2Texture : public QRhiTexture
+{
+ QGles2Texture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+ int sampleCount, Flags flags);
+ ~QGles2Texture();
+ void release() override;
+ bool build() override;
+ bool buildFrom(const QRhiNativeHandles *src) override;
+ const QRhiNativeHandles *nativeHandles() override;
+
+ bool prepareBuild(QSize *adjustedSize = nullptr);
+
+ GLuint texture = 0;
+ bool owns = true;
+ GLenum target;
+ GLenum glintformat;
+ GLenum glformat;
+ GLenum gltype;
+ QGles2SamplerData samplerState;
+ bool specified = false;
+ int mipLevelCount = 0;
+ QRhiGles2TextureNativeHandles nativeHandlesStruct;
+
+ uint generation = 0;
+ friend class QRhiGles2;
+};
+
+struct QGles2Sampler : public QRhiSampler
+{
+ QGles2Sampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
+ AddressMode u, AddressMode v);
+ ~QGles2Sampler();
+ void release() override;
+ bool build() override;
+
+ QGles2SamplerData d;
+ uint generation = 0;
+ friend class QRhiGles2;
+};
+
+struct QGles2RenderPassDescriptor : public QRhiRenderPassDescriptor
+{
+ QGles2RenderPassDescriptor(QRhiImplementation *rhi);
+ ~QGles2RenderPassDescriptor();
+ void release() override;
+};
+
+struct QGles2RenderTargetData
+{
+ QGles2RenderTargetData(QRhiImplementation *) { }
+
+ QGles2RenderPassDescriptor *rp = nullptr;
+ QSize pixelSize;
+ float dpr = 1;
+ int sampleCount = 1;
+ int colorAttCount = 0;
+ int dsAttCount = 0;
+ bool srgbUpdateAndBlend = false;
+};
+
+struct QGles2ReferenceRenderTarget : public QRhiRenderTarget
+{
+ QGles2ReferenceRenderTarget(QRhiImplementation *rhi);
+ ~QGles2ReferenceRenderTarget();
+ void release() override;
+
+ QSize pixelSize() const override;
+ float devicePixelRatio() const override;
+ int sampleCount() const override;
+
+ QGles2RenderTargetData d;
+};
+
+struct QGles2TextureRenderTarget : public QRhiTextureRenderTarget
+{
+ QGles2TextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc, Flags flags);
+ ~QGles2TextureRenderTarget();
+ void release() override;
+
+ QSize pixelSize() const override;
+ float devicePixelRatio() const override;
+ int sampleCount() const override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+ bool build() override;
+
+ QGles2RenderTargetData d;
+ GLuint framebuffer = 0;
+ friend class QRhiGles2;
+};
+
+struct QGles2ShaderResourceBindings : public QRhiShaderResourceBindings
+{
+ QGles2ShaderResourceBindings(QRhiImplementation *rhi);
+ ~QGles2ShaderResourceBindings();
+ void release() override;
+ bool build() override;
+
+ uint generation = 0;
+ friend class QRhiGles2;
+};
+
+struct QGles2GraphicsPipeline : public QRhiGraphicsPipeline
+{
+ QGles2GraphicsPipeline(QRhiImplementation *rhi);
+ ~QGles2GraphicsPipeline();
+ void release() override;
+ bool build() override;
+
+ GLuint program = 0;
+ GLenum drawMode = GL_TRIANGLES;
+ QShaderDescription vsDesc;
+ QShaderDescription fsDesc;
+ bool canUseUniformBuffers = false;
+
+ struct Uniform {
+ QShaderDescription::VariableType type;
+ int glslLocation;
+ int binding;
+ uint offset;
+ int size;
+ };
+ QVector<Uniform> uniforms;
+
+ struct Sampler {
+ int glslLocation;
+ int binding;
+ };
+ QVector<Sampler> samplers;
+
+ uint generation = 0;
+ friend class QRhiGles2;
+};
+
+Q_DECLARE_TYPEINFO(QGles2GraphicsPipeline::Uniform, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QGles2GraphicsPipeline::Sampler, Q_MOVABLE_TYPE);
+
+struct QGles2ComputePipeline : public QRhiComputePipeline
+{
+ QGles2ComputePipeline(QRhiImplementation *rhi);
+ ~QGles2ComputePipeline();
+ void release() override;
+ bool build() override;
+};
+
+struct QGles2CommandBuffer : public QRhiCommandBuffer
+{
+ QGles2CommandBuffer(QRhiImplementation *rhi);
+ ~QGles2CommandBuffer();
+ void release() override;
+
+ struct Command {
+ enum Cmd {
+ BeginFrame,
+ EndFrame,
+ Viewport,
+ Scissor,
+ BlendConstants,
+ StencilRef,
+ BindVertexBuffer,
+ BindIndexBuffer,
+ Draw,
+ DrawIndexed,
+ BindGraphicsPipeline,
+ BindShaderResources,
+ BindFramebuffer,
+ Clear,
+ BufferData,
+ BufferSubData,
+ CopyTex,
+ ReadPixels,
+ SubImage,
+ CompressedImage,
+ CompressedSubImage,
+ BlitFromRenderbuffer,
+ GenMip
+ };
+ Cmd cmd;
+
+ static const int MAX_UBUF_BINDINGS = 32; // should be more than enough
+
+ // QRhi*/QGles2* references should be kept at minimum (so no
+ // QRhiTexture/Buffer/etc. pointers).
+ union {
+ struct {
+ float x, y, w, h;
+ float d0, d1;
+ } viewport;
+ struct {
+ int x, y, w, h;
+ } scissor;
+ struct {
+ float r, g, b, a;
+ } blendConstants;
+ struct {
+ quint32 ref;
+ QRhiGraphicsPipeline *ps;
+ } stencilRef;
+ struct {
+ QRhiGraphicsPipeline *ps;
+ GLuint buffer;
+ quint32 offset;
+ int binding;
+ } bindVertexBuffer;
+ struct {
+ GLuint buffer;
+ quint32 offset;
+ GLenum type;
+ } bindIndexBuffer;
+ struct {
+ QRhiGraphicsPipeline *ps;
+ quint32 vertexCount;
+ quint32 firstVertex;
+ } draw;
+ struct {
+ QRhiGraphicsPipeline *ps;
+ quint32 indexCount;
+ quint32 firstIndex;
+ } drawIndexed;
+ struct {
+ QRhiGraphicsPipeline *ps;
+ } bindGraphicsPipeline;
+ struct {
+ QRhiGraphicsPipeline *ps;
+ QRhiShaderResourceBindings *srb;
+ int dynamicOffsetCount;
+ uint dynamicOffsetPairs[MAX_UBUF_BINDINGS * 2]; // binding, offsetInConstants
+ } bindShaderResources;
+ struct {
+ GLbitfield mask;
+ float c[4];
+ float d;
+ quint32 s;
+ } clear;
+ struct {
+ GLuint fbo;
+ bool srgb;
+ int colorAttCount;
+ } bindFramebuffer;
+ struct {
+ GLenum target;
+ GLuint buffer;
+ int offset;
+ int size;
+ const void *data; // must come from retainData()
+ } bufferSubData;
+ struct {
+ GLenum srcFaceTarget;
+ GLuint srcTexture;
+ int srcLevel;
+ int srcX;
+ int srcY;
+ GLenum dstTarget;
+ GLuint dstTexture;
+ GLenum dstFaceTarget;
+ int dstLevel;
+ int dstX;
+ int dstY;
+ int w;
+ int h;
+ } copyTex;
+ struct {
+ QRhiReadbackResult *result;
+ GLuint texture;
+ int w;
+ int h;
+ QRhiTexture::Format format;
+ GLenum readTarget;
+ int level;
+ } readPixels;
+ struct {
+ GLenum target;
+ GLuint texture;
+ GLenum faceTarget;
+ int level;
+ int dx;
+ int dy;
+ int w;
+ int h;
+ GLenum glformat;
+ GLenum gltype;
+ int rowStartAlign;
+ const void *data; // must come from retainImage()
+ } subImage;
+ struct {
+ GLenum target;
+ GLuint texture;
+ GLenum faceTarget;
+ int level;
+ GLenum glintformat;
+ int w;
+ int h;
+ int size;
+ const void *data; // must come from retainData()
+ } compressedImage;
+ struct {
+ GLenum target;
+ GLuint texture;
+ GLenum faceTarget;
+ int level;
+ int dx;
+ int dy;
+ int w;
+ int h;
+ GLenum glintformat;
+ int size;
+ const void *data; // must come from retainData()
+ } compressedSubImage;
+ struct {
+ GLuint renderbuffer;
+ int w;
+ int h;
+ GLenum target;
+ GLuint texture;
+ int dstLevel;
+ } blitFromRb;
+ struct {
+ GLenum target;
+ GLuint texture;
+ } genMip;
+ } args;
+ };
+
+ enum PassType {
+ NoPass,
+ RenderPass,
+ ComputePass
+ };
+
+ QVector<Command> commands;
+ PassType recordingPass;
+ QRhiRenderTarget *currentTarget;
+ QRhiGraphicsPipeline *currentPipeline;
+ uint currentPipelineGeneration;
+ QRhiShaderResourceBindings *currentSrb;
+ uint currentSrbGeneration;
+
+ QVector<QByteArray> dataRetainPool;
+ QVector<QImage> imageRetainPool;
+
+ // relies heavily on implicit sharing (no copies of the actual data will be made)
+ const void *retainData(const QByteArray &data) {
+ dataRetainPool.append(data);
+ return dataRetainPool.constLast().constData();
+ }
+ const void *retainImage(const QImage &image) {
+ imageRetainPool.append(image);
+ return imageRetainPool.constLast().constBits();
+ }
+ void resetCommands() {
+ commands.clear();
+ dataRetainPool.clear();
+ imageRetainPool.clear();
+ }
+ void resetState() {
+ resetCommands();
+ recordingPass = NoPass;
+ currentTarget = nullptr;
+ resetCachedState();
+ }
+ void resetCachedState() {
+ currentPipeline = nullptr;
+ currentPipelineGeneration = 0;
+ currentSrb = nullptr;
+ currentSrbGeneration = 0;
+ }
+};
+
+Q_DECLARE_TYPEINFO(QGles2CommandBuffer::Command, Q_MOVABLE_TYPE);
+
+struct QGles2SwapChain : public QRhiSwapChain
+{
+ QGles2SwapChain(QRhiImplementation *rhi);
+ ~QGles2SwapChain();
+ void release() override;
+
+ QRhiCommandBuffer *currentFrameCommandBuffer() override;
+ QRhiRenderTarget *currentFrameRenderTarget() override;
+
+ QSize surfacePixelSize() override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+ bool buildOrResize() override;
+
+ QSurface *surface = nullptr;
+ QSize pixelSize;
+ QGles2ReferenceRenderTarget rt;
+ QGles2CommandBuffer cb;
+ int frameCount = 0;
+};
+
+class QRhiGles2 : public QRhiImplementation
+{
+public:
+ QRhiGles2(QRhiGles2InitParams *params, QRhiGles2NativeHandles *importDevice = nullptr);
+
+ bool create(QRhi::Flags flags) override;
+ void destroy() override;
+
+ QRhiGraphicsPipeline *createGraphicsPipeline() override;
+ QRhiComputePipeline *createComputePipeline() override;
+ QRhiShaderResourceBindings *createShaderResourceBindings() override;
+ QRhiBuffer *createBuffer(QRhiBuffer::Type type,
+ QRhiBuffer::UsageFlags usage,
+ int size) override;
+ QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiRenderBuffer::Flags flags) override;
+ QRhiTexture *createTexture(QRhiTexture::Format format,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiTexture::Flags flags) override;
+ QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler:: AddressMode u, QRhiSampler::AddressMode v) override;
+
+ QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
+ QRhiTextureRenderTarget::Flags flags) override;
+
+ QRhiSwapChain *createSwapChain() override;
+ QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
+ QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
+ QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override;
+ QRhi::FrameOpResult endOffscreenFrame() override;
+ QRhi::FrameOpResult finish() override;
+
+ void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+
+ void beginPass(QRhiCommandBuffer *cb,
+ QRhiRenderTarget *rt,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ QRhiResourceUpdateBatch *resourceUpdates) override;
+ void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+
+ void setGraphicsPipeline(QRhiCommandBuffer *cb,
+ QRhiGraphicsPipeline *ps) override;
+
+ void setShaderResources(QRhiCommandBuffer *cb,
+ QRhiShaderResourceBindings *srb,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) override;
+
+ void setVertexInput(QRhiCommandBuffer *cb,
+ int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
+ QRhiBuffer *indexBuf, quint32 indexOffset,
+ QRhiCommandBuffer::IndexFormat indexFormat) override;
+
+ void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) override;
+ void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override;
+ void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
+ void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
+
+ void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
+ quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
+
+ void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
+ quint32 instanceCount, quint32 firstIndex,
+ qint32 vertexOffset, quint32 firstInstance) override;
+
+ void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) override;
+ void debugMarkEnd(QRhiCommandBuffer *cb) override;
+ void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override;
+
+ void beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+ void endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+ void setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) override;
+ void dispatch(QRhiCommandBuffer *cb, int x, int y, int z) override;
+
+ const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) override;
+ void beginExternal(QRhiCommandBuffer *cb) override;
+ void endExternal(QRhiCommandBuffer *cb) override;
+
+ QVector<int> supportedSampleCounts() const override;
+ int ubufAlignment() const override;
+ bool isYUpInFramebuffer() const override;
+ bool isYUpInNDC() const override;
+ bool isClipDepthZeroToOne() const override;
+ QMatrix4x4 clipSpaceCorrMatrix() const override;
+ bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const override;
+ bool isFeatureSupported(QRhi::Feature feature) const override;
+ int resourceLimit(QRhi::ResourceLimit limit) const override;
+ const QRhiNativeHandles *nativeHandles() override;
+ void sendVMemStatsToProfiler() override;
+ void makeThreadLocalNativeContextCurrent() override;
+
+ bool ensureContext(QSurface *surface = nullptr) const;
+ void executeDeferredReleases();
+ QRhi::FrameOpResult flushCommandBuffer();
+ void enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cbD,
+ int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc);
+ void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates);
+ void executeCommandBuffer(QRhiCommandBuffer *cb);
+ void executeBindGraphicsPipeline(QRhiGraphicsPipeline *ps);
+ void bindShaderResources(QRhiGraphicsPipeline *ps, QRhiShaderResourceBindings *srb,
+ const uint *dynOfsPairs, int dynOfsCount);
+ QGles2RenderTargetData *enqueueBindFramebuffer(QRhiRenderTarget *rt, QGles2CommandBuffer *cbD,
+ bool *wantsColorClear = nullptr, bool *wantsDsClear = nullptr);
+ int effectiveSampleCount(int sampleCount) const;
+
+ QOpenGLContext *ctx = nullptr;
+ bool importedContext = false;
+ QSurfaceFormat requestedFormat;
+ QSurface *fallbackSurface = nullptr;
+ QWindow *maybeWindow = nullptr;
+ mutable bool needsMakeCurrent = false;
+ QOpenGLExtensions *f = nullptr;
+ uint vao = 0;
+ struct Caps {
+ Caps()
+ : ctxMajor(2),
+ ctxMinor(0),
+ maxTextureSize(2048),
+ maxDrawBuffers(4),
+ msaaRenderBuffer(false),
+ npotTexture(true),
+ npotTextureRepeat(true),
+ gles(false),
+ fixedIndexPrimitiveRestart(false),
+ bgraExternalFormat(false),
+ bgraInternalFormat(false),
+ r8Format(false),
+ r16Format(false),
+ floatFormats(false),
+ depthTexture(false),
+ packedDepthStencil(false),
+ srgbCapableDefaultFramebuffer(false),
+ coreProfile(false),
+ uniformBuffers(false),
+ elementIndexUint(false)
+ { }
+ int ctxMajor;
+ int ctxMinor;
+ int maxTextureSize;
+ int maxDrawBuffers;
+ int maxSamples;
+ // Multisample fb and blit are supported (GLES 3.0 or OpenGL 3.x). Not
+ // the same as multisample textures!
+ uint msaaRenderBuffer : 1;
+ uint npotTexture : 1;
+ uint npotTextureRepeat : 1;
+ uint gles : 1;
+ uint fixedIndexPrimitiveRestart : 1;
+ uint bgraExternalFormat : 1;
+ uint bgraInternalFormat : 1;
+ uint r8Format : 1;
+ uint r16Format : 1;
+ uint floatFormats : 1;
+ uint depthTexture : 1;
+ uint packedDepthStencil : 1;
+ uint srgbCapableDefaultFramebuffer : 1;
+ uint coreProfile : 1;
+ uint uniformBuffers : 1;
+ uint elementIndexUint : 1;
+ } caps;
+ QGles2SwapChain *currentSwapChain = nullptr;
+ QVector<GLint> supportedCompressedFormats;
+ mutable QVector<int> supportedSampleCountList;
+ QRhiGles2NativeHandles nativeHandlesStruct;
+
+ struct DeferredReleaseEntry {
+ enum Type {
+ Buffer,
+ Pipeline,
+ Texture,
+ RenderBuffer,
+ TextureRenderTarget
+ };
+ Type type;
+ union {
+ struct {
+ GLuint buffer;
+ } buffer;
+ struct {
+ GLuint program;
+ } pipeline;
+ struct {
+ GLuint texture;
+ } texture;
+ struct {
+ GLuint renderbuffer;
+ GLuint renderbuffer2;
+ } renderbuffer;
+ struct {
+ GLuint framebuffer;
+ } textureRenderTarget;
+ };
+ };
+ QVector<DeferredReleaseEntry> releaseQueue;
+
+ struct OffscreenFrame {
+ OffscreenFrame(QRhiImplementation *rhi) : cbWrapper(rhi) { }
+ bool active = false;
+ QGles2CommandBuffer cbWrapper;
+ } ofr;
+};
+
+Q_DECLARE_TYPEINFO(QRhiGles2::DeferredReleaseEntry, Q_MOVABLE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm
new file mode 100644
index 0000000000..214374e0c6
--- /dev/null
+++ b/src/gui/rhi/qrhimetal.mm
@@ -0,0 +1,3558 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qrhimetal_p_p.h"
+#include "qshader_p.h"
+#include "qshaderdescription_p.h"
+#include <QGuiApplication>
+#include <QWindow>
+#include <qmath.h>
+
+#ifdef Q_OS_MACOS
+#include <AppKit/AppKit.h>
+#endif
+
+#include <Metal/Metal.h>
+#include <QuartzCore/CAMetalLayer.h>
+
+QT_BEGIN_NAMESPACE
+
+/*
+ Metal backend. Double buffers and throttles to vsync. "Dynamic" buffers are
+ Shared (host visible) and duplicated (to help having 2 frames in flight),
+ "static" and "immutable" are Managed on macOS and Shared on iOS/tvOS.
+ Textures are Private (device local) and a host visible staging buffer is
+ used to upload data to them. Does not rely on strong objects refs from
+ command buffers but does rely on the automatic resource tracking of the
+ command encoders.
+*/
+
+#if __has_feature(objc_arc)
+#error ARC not supported
+#endif
+
+// Note: we expect everything here pass the Metal API validation when running
+// in Debug mode in XCode. Some of the issues that break validation are not
+// obvious and not visible when running outside XCode.
+//
+// An exception is the nextDrawable Called Early blah blah warning, which is
+// plain and simply false.
+
+/*!
+ \class QRhiMetalInitParams
+ \inmodule QtRhi
+ \brief Metal specific initialization parameters.
+
+ A Metal-based QRhi needs no special parameters for initialization.
+
+ \badcode
+ QRhiMetalInitParams params;
+ rhi = QRhi::create(QRhi::Metal, &params);
+ \endcode
+
+ \note Metal API validation cannot be enabled by the application. Instead,
+ run the debug build of the application in XCode. Generating a
+ \c{.xcodeproj} file via \c{qmake -spec macx-xcode} provides a convenient
+ way to enable this.
+
+ \note QRhiSwapChain can only target QWindow instances that have their
+ surface type set to QSurface::MetalSurface.
+
+ \section2 Working with existing Metal devices
+
+ When interoperating with another graphics engine, it may be necessary to
+ get a QRhi instance that uses the same Metal device. This can be achieved
+ by passing a pointer to a QRhiMetalNativeHandles to QRhi::create(). The
+ device must be set to a non-null value then. Optionally, a command queue
+ object can be specified as well.
+
+ The QRhi does not take ownership of any of the external objects.
+ */
+
+/*!
+ \class QRhiMetalNativeHandles
+ \inmodule QtRhi
+ \brief Holds the Metal device used by the QRhi.
+
+ \note The class uses \c{void *} as the type since including the Objective C
+ headers is not acceptable here. The actual types are \c{id<MTLDevice>} and
+ \c{id<MTLCommandQueue>}.
+ */
+
+/*!
+ \class QRhiMetalTextureNativeHandles
+ \inmodule QtRhi
+ \brief Holds the Metal texture object that is backing a QRhiTexture instance.
+
+ \note The class uses \c{void *} as the type since including the Objective C
+ headers is not acceptable here. The actual type is \c{id<MTLTexture>}.
+ */
+
+/*!
+ \class QRhiMetalCommandBufferNativeHandles
+ \inmodule QtRhi
+ \brief Holds the MTLCommandBuffer and MTLRenderCommandEncoder objects that are backing a QRhiCommandBuffer.
+
+ \note The command buffer object is only guaranteed to be valid while
+ recording a frame, that is, between a \l{QRhi::beginFrame()}{beginFrame()}
+ - \l{QRhi::endFrame()}{endFrame()} or
+ \l{QRhi::beginOffscreenFrame()}{beginOffscreenFrame()} -
+ \l{QRhi::endOffsrceenFrame()}{endOffscreenFrame()} pair.
+
+ \note The command encoder is only valid while recording a pass, that is,
+ between \l{QRhiCommandBuffer::beginPass()} -
+ \l{QRhiCommandBuffer::endPass()}.
+ */
+
+struct QRhiMetalData
+{
+ QRhiMetalData(QRhiImplementation *rhi) : ofr(rhi) { }
+
+ id<MTLDevice> dev = nil;
+ id<MTLCommandQueue> cmdQueue = nil;
+
+ MTLRenderPassDescriptor *createDefaultRenderPass(bool hasDepthStencil,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ int colorAttCount);
+ id<MTLLibrary> createMetalLib(const QShader &shader, QShader::Variant shaderVariant,
+ QString *error, QByteArray *entryPoint);
+ id<MTLFunction> createMSLShaderFunction(id<MTLLibrary> lib, const QByteArray &entryPoint);
+
+ struct DeferredReleaseEntry {
+ enum Type {
+ Buffer,
+ RenderBuffer,
+ Texture,
+ Sampler,
+ StagingBuffer
+ };
+ Type type;
+ int lastActiveFrameSlot; // -1 if not used otherwise 0..FRAMES_IN_FLIGHT-1
+ union {
+ struct {
+ id<MTLBuffer> buffers[QMTL_FRAMES_IN_FLIGHT];
+ } buffer;
+ struct {
+ id<MTLTexture> texture;
+ } renderbuffer;
+ struct {
+ id<MTLTexture> texture;
+ id<MTLBuffer> stagingBuffers[QMTL_FRAMES_IN_FLIGHT];
+ id<MTLTexture> views[QRhi::MAX_LEVELS];
+ } texture;
+ struct {
+ id<MTLSamplerState> samplerState;
+ } sampler;
+ struct {
+ id<MTLBuffer> buffer;
+ } stagingBuffer;
+ };
+ };
+ QVector<DeferredReleaseEntry> releaseQueue;
+
+ struct OffscreenFrame {
+ OffscreenFrame(QRhiImplementation *rhi) : cbWrapper(rhi) { }
+ bool active = false;
+ QMetalCommandBuffer cbWrapper;
+ } ofr;
+
+ struct ActiveReadback {
+ int activeFrameSlot = -1;
+ QRhiReadbackDescription desc;
+ QRhiReadbackResult *result;
+ id<MTLBuffer> buf;
+ quint32 bufSize;
+ QSize pixelSize;
+ QRhiTexture::Format format;
+ };
+ QVector<ActiveReadback> activeReadbacks;
+
+ API_AVAILABLE(macos(10.13), ios(11.0)) MTLCaptureManager *captureMgr;
+ API_AVAILABLE(macos(10.13), ios(11.0)) id<MTLCaptureScope> captureScope = nil;
+
+ static const int TEXBUF_ALIGN = 256; // probably not accurate
+};
+
+Q_DECLARE_TYPEINFO(QRhiMetalData::DeferredReleaseEntry, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QRhiMetalData::ActiveReadback, Q_MOVABLE_TYPE);
+
+struct QMetalBufferData
+{
+ bool managed;
+ bool slotted;
+ id<MTLBuffer> buf[QMTL_FRAMES_IN_FLIGHT];
+ QVector<QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate> pendingUpdates[QMTL_FRAMES_IN_FLIGHT];
+};
+
+struct QMetalRenderBufferData
+{
+ MTLPixelFormat format;
+ id<MTLTexture> tex = nil;
+};
+
+struct QMetalTextureData
+{
+ QMetalTextureData(QMetalTexture *t) : q(t) { }
+
+ QMetalTexture *q;
+ MTLPixelFormat format;
+ id<MTLTexture> tex = nil;
+ id<MTLBuffer> stagingBuf[QMTL_FRAMES_IN_FLIGHT];
+ bool owns = true;
+ id<MTLTexture> perLevelViews[QRhi::MAX_LEVELS];
+
+ id<MTLTexture> viewForLevel(int level);
+};
+
+struct QMetalSamplerData
+{
+ id<MTLSamplerState> samplerState = nil;
+};
+
+struct QMetalCommandBufferData
+{
+ id<MTLCommandBuffer> cb;
+ id<MTLRenderCommandEncoder> currentRenderPassEncoder;
+ id<MTLComputeCommandEncoder> currentComputePassEncoder;
+ MTLRenderPassDescriptor *currentPassRpDesc;
+ int currentFirstVertexBinding;
+ QRhiBatchedBindings<id<MTLBuffer> > currentVertexInputsBuffers;
+ QRhiBatchedBindings<NSUInteger> currentVertexInputOffsets;
+};
+
+struct QMetalRenderTargetData
+{
+ QSize pixelSize;
+ float dpr = 1;
+ int sampleCount = 1;
+ int colorAttCount = 0;
+ int dsAttCount = 0;
+
+ struct ColorAtt {
+ bool needsDrawableForTex = false;
+ id<MTLTexture> tex = nil;
+ int layer = 0;
+ int level = 0;
+ bool needsDrawableForResolveTex = false;
+ id<MTLTexture> resolveTex = nil;
+ int resolveLayer = 0;
+ int resolveLevel = 0;
+ };
+
+ struct {
+ ColorAtt colorAtt[QMetalRenderPassDescriptor::MAX_COLOR_ATTACHMENTS];
+ id<MTLTexture> dsTex = nil;
+ bool hasStencil = false;
+ bool depthNeedsStore = false;
+ } fb;
+};
+
+struct QMetalGraphicsPipelineData
+{
+ id<MTLRenderPipelineState> ps = nil;
+ id<MTLDepthStencilState> ds = nil;
+ MTLPrimitiveType primitiveType;
+ MTLWinding winding;
+ MTLCullMode cullMode;
+ id<MTLLibrary> vsLib = nil;
+ id<MTLFunction> vsFunc = nil;
+ id<MTLLibrary> fsLib = nil;
+ id<MTLFunction> fsFunc = nil;
+};
+
+struct QMetalComputePipelineData
+{
+ id<MTLComputePipelineState> ps = nil;
+ id<MTLLibrary> csLib = nil;
+ id<MTLFunction> csFunc = nil;
+ MTLSize localSize;
+};
+
+struct QMetalSwapChainData
+{
+ CAMetalLayer *layer = nullptr;
+ id<CAMetalDrawable> curDrawable;
+ dispatch_semaphore_t sem[QMTL_FRAMES_IN_FLIGHT];
+ MTLRenderPassDescriptor *rp = nullptr;
+ id<MTLTexture> msaaTex[QMTL_FRAMES_IN_FLIGHT];
+ QRhiTexture::Format rhiColorFormat;
+ MTLPixelFormat colorFormat;
+};
+
+QRhiMetal::QRhiMetal(QRhiMetalInitParams *params, QRhiMetalNativeHandles *importDevice)
+{
+ Q_UNUSED(params);
+
+ d = new QRhiMetalData(this);
+
+ importedDevice = importDevice != nullptr;
+ if (importedDevice) {
+ if (d->dev) {
+ d->dev = (id<MTLDevice>) importDevice->dev;
+ importedCmdQueue = importDevice->cmdQueue != nullptr;
+ if (importedCmdQueue)
+ d->cmdQueue = (id<MTLCommandQueue>) importDevice->cmdQueue;
+ } else {
+ qWarning("No MTLDevice given, cannot import");
+ importedDevice = false;
+ }
+ }
+}
+
+QRhiMetal::~QRhiMetal()
+{
+ delete d;
+}
+
+static inline uint aligned(uint v, uint byteAlign)
+{
+ return (v + byteAlign - 1) & ~(byteAlign - 1);
+}
+
+bool QRhiMetal::create(QRhi::Flags flags)
+{
+ Q_UNUSED(flags);
+
+ if (importedDevice)
+ [d->dev retain];
+ else
+ d->dev = MTLCreateSystemDefaultDevice();
+
+ qDebug("Metal device: %s", qPrintable(QString::fromNSString([d->dev name])));
+
+ if (importedCmdQueue)
+ [d->cmdQueue retain];
+ else
+ d->cmdQueue = [d->dev newCommandQueue];
+
+ if (@available(macOS 10.13, iOS 11.0, *)) {
+ d->captureMgr = [MTLCaptureManager sharedCaptureManager];
+ // Have a custom capture scope as well which then shows up in XCode as
+ // an option when capturing, and becomes especially useful when having
+ // multiple windows with multiple QRhis.
+ d->captureScope = [d->captureMgr newCaptureScopeWithCommandQueue: d->cmdQueue];
+ const QString label = QString::asprintf("Qt capture scope for QRhi %p", this);
+ d->captureScope.label = label.toNSString();
+ }
+
+#if defined(Q_OS_MACOS)
+ caps.maxTextureSize = 16384;
+#elif defined(Q_OS_TVOS)
+ if ([d->dev supportsFeatureSet: MTLFeatureSet(30003)]) // MTLFeatureSet_tvOS_GPUFamily2_v1
+ caps.maxTextureSize = 16384;
+ else
+ caps.maxTextureSize = 8192;
+#elif defined(Q_OS_IOS)
+ // welcome to feature set hell
+ if ([d->dev supportsFeatureSet: MTLFeatureSet(16)] // MTLFeatureSet_iOS_GPUFamily5_v1
+ || [d->dev supportsFeatureSet: MTLFeatureSet(11)] // MTLFeatureSet_iOS_GPUFamily4_v1
+ || [d->dev supportsFeatureSet: MTLFeatureSet(4)]) // MTLFeatureSet_iOS_GPUFamily3_v1
+ {
+ caps.maxTextureSize = 16384;
+ } else if ([d->dev supportsFeatureSet: MTLFeatureSet(3)] // MTLFeatureSet_iOS_GPUFamily2_v2
+ || [d->dev supportsFeatureSet: MTLFeatureSet(2)]) // MTLFeatureSet_iOS_GPUFamily1_v2
+ {
+ caps.maxTextureSize = 8192;
+ } else {
+ caps.maxTextureSize = 4096;
+ }
+#endif
+
+ nativeHandlesStruct.dev = d->dev;
+ nativeHandlesStruct.cmdQueue = d->cmdQueue;
+
+ return true;
+}
+
+void QRhiMetal::destroy()
+{
+ executeDeferredReleases(true);
+ finishActiveReadbacks(true);
+
+ if (@available(macOS 10.13, iOS 11.0, *)) {
+ [d->captureScope release];
+ d->captureScope = nil;
+ }
+
+ [d->cmdQueue release];
+ if (!importedCmdQueue)
+ d->cmdQueue = nil;
+
+ [d->dev release];
+ if (!importedDevice)
+ d->dev = nil;
+}
+
+QVector<int> QRhiMetal::supportedSampleCounts() const
+{
+ return { 1, 2, 4, 8 };
+}
+
+int QRhiMetal::effectiveSampleCount(int sampleCount) const
+{
+ // Stay compatible with QSurfaceFormat and friends where samples == 0 means the same as 1.
+ const int s = qBound(1, sampleCount, 64);
+ if (!supportedSampleCounts().contains(s)) {
+ qWarning("Attempted to set unsupported sample count %d", sampleCount);
+ return 1;
+ }
+ return s;
+}
+
+QRhiSwapChain *QRhiMetal::createSwapChain()
+{
+ return new QMetalSwapChain(this);
+}
+
+QRhiBuffer *QRhiMetal::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size)
+{
+ return new QMetalBuffer(this, type, usage, size);
+}
+
+int QRhiMetal::ubufAlignment() const
+{
+ return 256;
+}
+
+bool QRhiMetal::isYUpInFramebuffer() const
+{
+ return false;
+}
+
+bool QRhiMetal::isYUpInNDC() const
+{
+ return true;
+}
+
+bool QRhiMetal::isClipDepthZeroToOne() const
+{
+ return true;
+}
+
+QMatrix4x4 QRhiMetal::clipSpaceCorrMatrix() const
+{
+ // depth range 0..1
+ static QMatrix4x4 m;
+ if (m.isIdentity()) {
+ // NB the ctor takes row-major
+ m = QMatrix4x4(1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 0.5f, 0.5f,
+ 0.0f, 0.0f, 0.0f, 1.0f);
+ }
+ return m;
+}
+
+bool QRhiMetal::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const
+{
+ Q_UNUSED(flags);
+
+#ifdef Q_OS_MACOS
+ if (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ETC2_RGBA8)
+ return false;
+ if (format >= QRhiTexture::ASTC_4x4 && format <= QRhiTexture::ASTC_12x12)
+ return false;
+#else
+ if (format >= QRhiTexture::BC1 && format <= QRhiTexture::BC7)
+ return false;
+#endif
+
+ return true;
+}
+
+bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const
+{
+ switch (feature) {
+ case QRhi::MultisampleTexture:
+ return true;
+ case QRhi::MultisampleRenderBuffer:
+ return true;
+ case QRhi::DebugMarkers:
+ return true;
+ case QRhi::Timestamps:
+ return false;
+ case QRhi::Instancing:
+ return true;
+ case QRhi::CustomInstanceStepRate:
+ return true;
+ case QRhi::PrimitiveRestart:
+ return true;
+ case QRhi::NonDynamicUniformBuffers:
+ return true;
+ case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
+ return false;
+ case QRhi::NPOTTextureRepeat:
+ return true;
+ case QRhi::RedOrAlpha8IsRed:
+ return true;
+ case QRhi::ElementIndexUint:
+ return true;
+ case QRhi::Compute:
+ return true;
+ default:
+ Q_UNREACHABLE();
+ return false;
+ }
+}
+
+int QRhiMetal::resourceLimit(QRhi::ResourceLimit limit) const
+{
+ switch (limit) {
+ case QRhi::TextureSizeMin:
+ return 1;
+ case QRhi::TextureSizeMax:
+ return caps.maxTextureSize;
+ case QRhi::MaxColorAttachments:
+ return 8;
+ case QRhi::FramesInFlight:
+ return QMTL_FRAMES_IN_FLIGHT;
+ default:
+ Q_UNREACHABLE();
+ return 0;
+ }
+}
+
+const QRhiNativeHandles *QRhiMetal::nativeHandles()
+{
+ return &nativeHandlesStruct;
+}
+
+void QRhiMetal::sendVMemStatsToProfiler()
+{
+ // nothing to do here
+}
+
+void QRhiMetal::makeThreadLocalNativeContextCurrent()
+{
+ // nothing to do here
+}
+
+QRhiRenderBuffer *QRhiMetal::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags)
+{
+ return new QMetalRenderBuffer(this, type, pixelSize, sampleCount, flags);
+}
+
+QRhiTexture *QRhiMetal::createTexture(QRhiTexture::Format format, const QSize &pixelSize,
+ int sampleCount, QRhiTexture::Flags flags)
+{
+ return new QMetalTexture(this, format, pixelSize, sampleCount, flags);
+}
+
+QRhiSampler *QRhiMetal::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler::AddressMode u, QRhiSampler::AddressMode v)
+{
+ return new QMetalSampler(this, magFilter, minFilter, mipmapMode, u, v);
+}
+
+QRhiTextureRenderTarget *QRhiMetal::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
+ QRhiTextureRenderTarget::Flags flags)
+{
+ return new QMetalTextureRenderTarget(this, desc, flags);
+}
+
+QRhiGraphicsPipeline *QRhiMetal::createGraphicsPipeline()
+{
+ return new QMetalGraphicsPipeline(this);
+}
+
+QRhiComputePipeline *QRhiMetal::createComputePipeline()
+{
+ return new QMetalComputePipeline(this);
+}
+
+QRhiShaderResourceBindings *QRhiMetal::createShaderResourceBindings()
+{
+ return new QMetalShaderResourceBindings(this);
+}
+
+void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD, QMetalCommandBuffer *cbD,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets,
+ bool offsetOnlyChange)
+{
+ static const int KNOWN_STAGES = 3;
+ struct {
+ QRhiBatchedBindings<id<MTLBuffer> > buffers;
+ QRhiBatchedBindings<NSUInteger> bufferOffsets;
+ QRhiBatchedBindings<id<MTLTexture> > textures;
+ QRhiBatchedBindings<id<MTLSamplerState> > samplers;
+ } res[KNOWN_STAGES];
+
+ for (const QRhiShaderResourceBinding &binding : qAsConst(srbD->sortedBindings)) {
+ const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&binding);
+ switch (b->type) {
+ case QRhiShaderResourceBinding::UniformBuffer:
+ {
+ QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.ubuf.buf);
+ id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
+ uint offset = b->u.ubuf.offset;
+ for (int i = 0; i < dynamicOffsetCount; ++i) {
+ const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
+ if (dynOfs.first == b->binding) {
+ offset = dynOfs.second;
+ break;
+ }
+ }
+ if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
+ res[0].buffers.feed(b->binding, mtlbuf);
+ res[0].bufferOffsets.feed(b->binding, offset);
+ }
+ if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
+ res[1].buffers.feed(b->binding, mtlbuf);
+ res[1].bufferOffsets.feed(b->binding, offset);
+ }
+ if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
+ res[2].buffers.feed(b->binding, mtlbuf);
+ res[2].bufferOffsets.feed(b->binding, offset);
+ }
+ }
+ break;
+ case QRhiShaderResourceBinding::SampledTexture:
+ {
+ QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.tex);
+ QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.sampler);
+ if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
+ res[0].textures.feed(b->binding, texD->d->tex);
+ res[0].samplers.feed(b->binding, samplerD->d->samplerState);
+ }
+ if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
+ res[1].textures.feed(b->binding, texD->d->tex);
+ res[1].samplers.feed(b->binding, samplerD->d->samplerState);
+ }
+ if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
+ res[2].textures.feed(b->binding, texD->d->tex);
+ res[2].samplers.feed(b->binding, samplerD->d->samplerState);
+ }
+ }
+ break;
+ case QRhiShaderResourceBinding::ImageLoad:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::ImageStore:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::ImageLoadStore:
+ {
+ QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.simage.tex);
+ id<MTLTexture> t = texD->d->viewForLevel(b->u.simage.level);
+ if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage))
+ res[0].textures.feed(b->binding, t);
+ if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage))
+ res[1].textures.feed(b->binding, t);
+ if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage))
+ res[2].textures.feed(b->binding, t);
+ }
+ break;
+ case QRhiShaderResourceBinding::BufferLoad:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::BufferStore:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::BufferLoadStore:
+ {
+ QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.sbuf.buf);
+ id<MTLBuffer> mtlbuf = bufD->d->buf[0];
+ uint offset = b->u.sbuf.offset;
+ if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) {
+ res[0].buffers.feed(b->binding, mtlbuf);
+ res[0].bufferOffsets.feed(b->binding, offset);
+ }
+ if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) {
+ res[1].buffers.feed(b->binding, mtlbuf);
+ res[1].bufferOffsets.feed(b->binding, offset);
+ }
+ if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) {
+ res[2].buffers.feed(b->binding, mtlbuf);
+ res[2].bufferOffsets.feed(b->binding, offset);
+ }
+ }
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+
+ for (int idx = 0; idx < KNOWN_STAGES; ++idx) {
+ res[idx].buffers.finish();
+ res[idx].bufferOffsets.finish();
+
+ for (int i = 0, ie = res[idx].buffers.batches.count(); i != ie; ++i) {
+ const auto &bufferBatch(res[idx].buffers.batches[i]);
+ const auto &offsetBatch(res[idx].bufferOffsets.batches[i]);
+ switch (idx) {
+ case 0:
+ [cbD->d->currentRenderPassEncoder setVertexBuffers: bufferBatch.resources.constData()
+ offsets: offsetBatch.resources.constData()
+ withRange: NSMakeRange(bufferBatch.startBinding, bufferBatch.resources.count())];
+ break;
+ case 1:
+ [cbD->d->currentRenderPassEncoder setFragmentBuffers: bufferBatch.resources.constData()
+ offsets: offsetBatch.resources.constData()
+ withRange: NSMakeRange(bufferBatch.startBinding, bufferBatch.resources.count())];
+ break;
+ case 2:
+ [cbD->d->currentComputePassEncoder setBuffers: bufferBatch.resources.constData()
+ offsets: offsetBatch.resources.constData()
+ withRange: NSMakeRange(bufferBatch.startBinding, bufferBatch.resources.count())];
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+
+ if (offsetOnlyChange)
+ continue;
+
+ res[idx].textures.finish();
+ res[idx].samplers.finish();
+
+ for (int i = 0, ie = res[idx].textures.batches.count(); i != ie; ++i) {
+ const auto &batch(res[idx].textures.batches[i]);
+ switch (idx) {
+ case 0:
+ [cbD->d->currentRenderPassEncoder setVertexTextures: batch.resources.constData()
+ withRange: NSMakeRange(batch.startBinding, batch.resources.count())];
+ break;
+ case 1:
+ [cbD->d->currentRenderPassEncoder setFragmentTextures: batch.resources.constData()
+ withRange: NSMakeRange(batch.startBinding, batch.resources.count())];
+ break;
+ case 2:
+ [cbD->d->currentComputePassEncoder setTextures: batch.resources.constData()
+ withRange: NSMakeRange(batch.startBinding, batch.resources.count())];
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+ for (int i = 0, ie = res[idx].samplers.batches.count(); i != ie; ++i) {
+ const auto &batch(res[idx].samplers.batches[i]);
+ switch (idx) {
+ case 0:
+ [cbD->d->currentRenderPassEncoder setVertexSamplerStates: batch.resources.constData()
+ withRange: NSMakeRange(batch.startBinding, batch.resources.count())];
+ break;
+ case 1:
+ [cbD->d->currentRenderPassEncoder setFragmentSamplerStates: batch.resources.constData()
+ withRange: NSMakeRange(batch.startBinding, batch.resources.count())];
+ break;
+ case 2:
+ [cbD->d->currentComputePassEncoder setSamplerStates: batch.resources.constData()
+ withRange: NSMakeRange(batch.startBinding, batch.resources.count())];
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+ }
+}
+
+void QRhiMetal::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps)
+{
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::RenderPass);
+ QMetalGraphicsPipeline *psD = QRHI_RES(QMetalGraphicsPipeline, ps);
+
+ if (cbD->currentGraphicsPipeline != ps || cbD->currentPipelineGeneration != psD->generation) {
+ cbD->currentGraphicsPipeline = ps;
+ cbD->currentComputePipeline = nullptr;
+ cbD->currentPipelineGeneration = psD->generation;
+
+ [cbD->d->currentRenderPassEncoder setRenderPipelineState: psD->d->ps];
+ [cbD->d->currentRenderPassEncoder setDepthStencilState: psD->d->ds];
+ [cbD->d->currentRenderPassEncoder setCullMode: psD->d->cullMode];
+ [cbD->d->currentRenderPassEncoder setFrontFacingWinding: psD->d->winding];
+ }
+
+ psD->lastActiveFrameSlot = currentFrameSlot;
+}
+
+void QRhiMetal::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
+{
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass != QMetalCommandBuffer::NoPass);
+ QMetalGraphicsPipeline *gfxPsD = QRHI_RES(QMetalGraphicsPipeline, cbD->currentGraphicsPipeline);
+ QMetalComputePipeline *compPsD = QRHI_RES(QMetalComputePipeline, cbD->currentComputePipeline);
+
+ if (!srb) {
+ if (gfxPsD)
+ srb = gfxPsD->m_shaderResourceBindings;
+ else
+ srb = compPsD->m_shaderResourceBindings;
+ }
+
+ QMetalShaderResourceBindings *srbD = QRHI_RES(QMetalShaderResourceBindings, srb);
+ bool hasSlottedResourceInSrb = false;
+ bool hasDynamicOffsetInSrb = false;
+ bool resNeedsRebind = false;
+
+ // do buffer writes, figure out if we need to rebind, and mark as in-use
+ for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
+ const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&srbD->sortedBindings[i]);
+ QMetalShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[i]);
+ switch (b->type) {
+ case QRhiShaderResourceBinding::UniformBuffer:
+ {
+ QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.ubuf.buf);
+ Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer));
+ executeBufferHostWritesForCurrentFrame(bufD);
+ if (bufD->d->slotted)
+ hasSlottedResourceInSrb = true;
+ if (b->u.ubuf.hasDynamicOffset)
+ hasDynamicOffsetInSrb = true;
+ if (bufD->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) {
+ resNeedsRebind = true;
+ bd.ubuf.id = bufD->m_id;
+ bd.ubuf.generation = bufD->generation;
+ }
+ bufD->lastActiveFrameSlot = currentFrameSlot;
+ }
+ break;
+ case QRhiShaderResourceBinding::SampledTexture:
+ {
+ QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.tex);
+ QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.sampler);
+ if (texD->generation != bd.stex.texGeneration
+ || texD->m_id != bd.stex.texId
+ || samplerD->generation != bd.stex.samplerGeneration
+ || samplerD->m_id != bd.stex.samplerId)
+ {
+ resNeedsRebind = true;
+ bd.stex.texId = texD->m_id;
+ bd.stex.texGeneration = texD->generation;
+ bd.stex.samplerId = samplerD->m_id;
+ bd.stex.samplerGeneration = samplerD->generation;
+ }
+ texD->lastActiveFrameSlot = currentFrameSlot;
+ samplerD->lastActiveFrameSlot = currentFrameSlot;
+ }
+ break;
+ case QRhiShaderResourceBinding::ImageLoad:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::ImageStore:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::ImageLoadStore:
+ {
+ QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.simage.tex);
+ if (texD->generation != bd.simage.generation || texD->m_id != bd.simage.id) {
+ resNeedsRebind = true;
+ bd.simage.id = texD->m_id;
+ bd.simage.generation = texD->generation;
+ }
+ texD->lastActiveFrameSlot = currentFrameSlot;
+ }
+ break;
+ case QRhiShaderResourceBinding::BufferLoad:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::BufferStore:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::BufferLoadStore:
+ {
+ QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.sbuf.buf);
+ Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer));
+ executeBufferHostWritesForCurrentFrame(bufD);
+ if (bufD->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) {
+ resNeedsRebind = true;
+ bd.sbuf.id = bufD->m_id;
+ bd.sbuf.generation = bufD->generation;
+ }
+ bufD->lastActiveFrameSlot = currentFrameSlot;
+ }
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+
+ // make sure the resources for the correct slot get bound
+ const int resSlot = hasSlottedResourceInSrb ? currentFrameSlot : 0;
+ if (hasSlottedResourceInSrb && cbD->currentResSlot != resSlot)
+ resNeedsRebind = true;
+
+ const bool srbChanged = gfxPsD ? (cbD->currentGraphicsSrb != srb) : (cbD->currentComputeSrb != srb);
+ const bool srbRebuilt = cbD->currentSrbGeneration != srbD->generation;
+
+ // dynamic uniform buffer offsets always trigger a rebind
+ if (hasDynamicOffsetInSrb || resNeedsRebind || srbChanged || srbRebuilt) {
+ if (gfxPsD) {
+ cbD->currentGraphicsSrb = srb;
+ cbD->currentComputeSrb = nullptr;
+ } else {
+ cbD->currentGraphicsSrb = nullptr;
+ cbD->currentComputeSrb = srb;
+ }
+ cbD->currentSrbGeneration = srbD->generation;
+ cbD->currentResSlot = resSlot;
+
+ const bool offsetOnlyChange = hasDynamicOffsetInSrb && !resNeedsRebind && !srbChanged && !srbRebuilt;
+ enqueueShaderResourceBindings(srbD, cbD, dynamicOffsetCount, dynamicOffsets, offsetOnlyChange);
+ }
+}
+
+void QRhiMetal::setVertexInput(QRhiCommandBuffer *cb,
+ int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
+ QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
+{
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::RenderPass);
+
+ QRhiBatchedBindings<id<MTLBuffer> > buffers;
+ QRhiBatchedBindings<NSUInteger> offsets;
+ for (int i = 0; i < bindingCount; ++i) {
+ QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, bindings[i].first);
+ executeBufferHostWritesForCurrentFrame(bufD);
+ bufD->lastActiveFrameSlot = currentFrameSlot;
+ id<MTLBuffer> mtlbuf = bufD->d->buf[bufD->d->slotted ? currentFrameSlot : 0];
+ buffers.feed(startBinding + i, mtlbuf);
+ offsets.feed(startBinding + i, bindings[i].second);
+ }
+ buffers.finish();
+ offsets.finish();
+
+ // same binding space for vertex and constant buffers - work it around
+ QRhiShaderResourceBindings *srb = cbD->currentGraphicsSrb;
+ // There's nothing guaranteeing setShaderResources() was called before
+ // setVertexInput()... but whatever srb will get bound will have to be
+ // layout-compatible anyways so maxBinding is the same.
+ if (!srb)
+ srb = cbD->currentGraphicsPipeline->shaderResourceBindings();
+ const int firstVertexBinding = QRHI_RES(QMetalShaderResourceBindings, srb)->maxBinding + 1;
+
+ if (firstVertexBinding != cbD->d->currentFirstVertexBinding
+ || buffers != cbD->d->currentVertexInputsBuffers
+ || offsets != cbD->d->currentVertexInputOffsets)
+ {
+ cbD->d->currentFirstVertexBinding = firstVertexBinding;
+ cbD->d->currentVertexInputsBuffers = buffers;
+ cbD->d->currentVertexInputOffsets = offsets;
+
+ for (int i = 0, ie = buffers.batches.count(); i != ie; ++i) {
+ const auto &bufferBatch(buffers.batches[i]);
+ const auto &offsetBatch(offsets.batches[i]);
+ [cbD->d->currentRenderPassEncoder setVertexBuffers:
+ bufferBatch.resources.constData()
+ offsets: offsetBatch.resources.constData()
+ withRange: NSMakeRange(firstVertexBinding + bufferBatch.startBinding, bufferBatch.resources.count())];
+ }
+ }
+
+ if (indexBuf) {
+ QMetalBuffer *ibufD = QRHI_RES(QMetalBuffer, indexBuf);
+ executeBufferHostWritesForCurrentFrame(ibufD);
+ ibufD->lastActiveFrameSlot = currentFrameSlot;
+ cbD->currentIndexBuffer = indexBuf;
+ cbD->currentIndexOffset = indexOffset;
+ cbD->currentIndexFormat = indexFormat;
+ } else {
+ cbD->currentIndexBuffer = nullptr;
+ }
+}
+
+void QRhiMetal::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport)
+{
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::RenderPass);
+ const QSize outputSize = cbD->currentTarget->pixelSize();
+
+ // x,y is top-left in MTLViewportRect but bottom-left in QRhiViewport
+ float x, y, w, h;
+ if (!qrhi_toTopLeftRenderTargetRect(outputSize, viewport.viewport(), &x, &y, &w, &h))
+ return;
+
+ MTLViewport vp;
+ vp.originX = x;
+ vp.originY = y;
+ vp.width = w;
+ vp.height = h;
+ vp.znear = viewport.minDepth();
+ vp.zfar = viewport.maxDepth();
+
+ [cbD->d->currentRenderPassEncoder setViewport: vp];
+
+ if (!QRHI_RES(QMetalGraphicsPipeline, cbD->currentGraphicsPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)) {
+ MTLScissorRect s;
+ s.x = x;
+ s.y = y;
+ s.width = w;
+ s.height = h;
+ [cbD->d->currentRenderPassEncoder setScissorRect: s];
+ }
+}
+
+void QRhiMetal::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor)
+{
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::RenderPass);
+ Q_ASSERT(QRHI_RES(QMetalGraphicsPipeline, cbD->currentGraphicsPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor));
+ const QSize outputSize = cbD->currentTarget->pixelSize();
+
+ // x,y is top-left in MTLScissorRect but bottom-left in QRhiScissor
+ int x, y, w, h;
+ if (!qrhi_toTopLeftRenderTargetRect(outputSize, scissor.scissor(), &x, &y, &w, &h))
+ return;
+
+ MTLScissorRect s;
+ s.x = x;
+ s.y = y;
+ s.width = w;
+ s.height = h;
+
+ [cbD->d->currentRenderPassEncoder setScissorRect: s];
+}
+
+void QRhiMetal::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
+{
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::RenderPass);
+
+ [cbD->d->currentRenderPassEncoder setBlendColorRed: c.redF() green: c.greenF() blue: c.blueF() alpha: c.alphaF()];
+}
+
+void QRhiMetal::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
+{
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::RenderPass);
+
+ [cbD->d->currentRenderPassEncoder setStencilReferenceValue: refValue];
+}
+
+void QRhiMetal::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
+ quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
+{
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::RenderPass);
+
+ [cbD->d->currentRenderPassEncoder drawPrimitives:
+ QRHI_RES(QMetalGraphicsPipeline, cbD->currentGraphicsPipeline)->d->primitiveType
+ vertexStart: firstVertex vertexCount: vertexCount instanceCount: instanceCount baseInstance: firstInstance];
+}
+
+void QRhiMetal::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
+ quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
+{
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::RenderPass);
+
+ if (!cbD->currentIndexBuffer)
+ return;
+
+ const quint32 indexOffset = cbD->currentIndexOffset + firstIndex * (cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? 2 : 4);
+ Q_ASSERT(indexOffset == aligned(indexOffset, 4));
+
+ QMetalBuffer *ibufD = QRHI_RES(QMetalBuffer, cbD->currentIndexBuffer);
+ id<MTLBuffer> mtlbuf = ibufD->d->buf[ibufD->d->slotted ? currentFrameSlot : 0];
+
+ [cbD->d->currentRenderPassEncoder drawIndexedPrimitives: QRHI_RES(QMetalGraphicsPipeline, cbD->currentGraphicsPipeline)->d->primitiveType
+ indexCount: indexCount
+ indexType: cbD->currentIndexFormat == QRhiCommandBuffer::IndexUInt16 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
+ indexBuffer: mtlbuf
+ indexBufferOffset: indexOffset
+ instanceCount: instanceCount
+ baseVertex: vertexOffset
+ baseInstance: firstInstance];
+}
+
+void QRhiMetal::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
+{
+ if (!debugMarkers)
+ return;
+
+ NSString *str = [NSString stringWithUTF8String: name.constData()];
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ if (cbD->recordingPass != QMetalCommandBuffer::NoPass) {
+ [cbD->d->currentRenderPassEncoder pushDebugGroup: str];
+ } else {
+ if (@available(macOS 10.13, iOS 11.0, *))
+ [cbD->d->cb pushDebugGroup: str];
+ }
+}
+
+void QRhiMetal::debugMarkEnd(QRhiCommandBuffer *cb)
+{
+ if (!debugMarkers)
+ return;
+
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ if (cbD->recordingPass != QMetalCommandBuffer::NoPass) {
+ [cbD->d->currentRenderPassEncoder popDebugGroup];
+ } else {
+ if (@available(macOS 10.13, iOS 11.0, *))
+ [cbD->d->cb popDebugGroup];
+ }
+}
+
+void QRhiMetal::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
+{
+ if (!debugMarkers)
+ return;
+
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ if (cbD->recordingPass != QMetalCommandBuffer::NoPass)
+ [cbD->d->currentRenderPassEncoder insertDebugSignpost: [NSString stringWithUTF8String: msg.constData()]];
+}
+
+const QRhiNativeHandles *QRhiMetal::nativeHandles(QRhiCommandBuffer *cb)
+{
+ return QRHI_RES(QMetalCommandBuffer, cb)->nativeHandles();
+}
+
+void QRhiMetal::beginExternal(QRhiCommandBuffer *cb)
+{
+ Q_UNUSED(cb);
+}
+
+void QRhiMetal::endExternal(QRhiCommandBuffer *cb)
+{
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ cbD->resetPerPassCachedState();
+}
+
+QRhi::FrameOpResult QRhiMetal::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags)
+{
+ Q_UNUSED(flags);
+
+ QMetalSwapChain *swapChainD = QRHI_RES(QMetalSwapChain, swapChain);
+
+ // This is a bit messed up since for this swapchain we want to wait for the
+ // commands+present to complete, while for others just for the commands
+ // (for this same frame slot) but not sure how to do that in a sane way so
+ // wait for full cb completion for now.
+ for (QMetalSwapChain *sc : qAsConst(swapchains)) {
+ dispatch_semaphore_t sem = sc->d->sem[swapChainD->currentFrameSlot];
+ dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
+ if (sc != swapChainD)
+ dispatch_semaphore_signal(sem);
+ }
+
+ currentSwapChain = swapChainD;
+ currentFrameSlot = swapChainD->currentFrameSlot;
+ if (swapChainD->ds)
+ swapChainD->ds->lastActiveFrameSlot = currentFrameSlot;
+
+ if (@available(macOS 10.13, iOS 11.0, *))
+ [d->captureScope beginScope];
+
+ // Do not let the command buffer mess with the refcount of objects. We do
+ // have a proper render loop and will manage lifetimes similarly to other
+ // backends (Vulkan).
+ swapChainD->cbWrapper.d->cb = [d->cmdQueue commandBufferWithUnretainedReferences];
+
+ QMetalRenderTargetData::ColorAtt colorAtt;
+ if (swapChainD->samples > 1) {
+ colorAtt.tex = swapChainD->d->msaaTex[currentFrameSlot];
+ colorAtt.needsDrawableForResolveTex = true;
+ } else {
+ colorAtt.needsDrawableForTex = true;
+ }
+
+ swapChainD->rtWrapper.d->fb.colorAtt[0] = colorAtt;
+ swapChainD->rtWrapper.d->fb.dsTex = swapChainD->ds ? swapChainD->ds->d->tex : nil;
+ swapChainD->rtWrapper.d->fb.hasStencil = swapChainD->ds ? true : false;
+ swapChainD->rtWrapper.d->fb.depthNeedsStore = false;
+
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+ QRHI_PROF_F(beginSwapChainFrame(swapChain));
+
+ executeDeferredReleases();
+ swapChainD->cbWrapper.resetState();
+ finishActiveReadbacks();
+
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiMetal::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags)
+{
+ QMetalSwapChain *swapChainD = QRHI_RES(QMetalSwapChain, swapChain);
+ Q_ASSERT(currentSwapChain == swapChainD);
+
+ const bool needsPresent = !flags.testFlag(QRhi::SkipPresent);
+ if (needsPresent) {
+ [swapChainD->cbWrapper.d->cb presentDrawable: swapChainD->d->curDrawable];
+ swapChainD->d->curDrawable = nil;
+ }
+
+ __block int thisFrameSlot = currentFrameSlot;
+ [swapChainD->cbWrapper.d->cb addCompletedHandler: ^(id<MTLCommandBuffer>) {
+ dispatch_semaphore_signal(swapChainD->d->sem[thisFrameSlot]);
+ }];
+
+ [swapChainD->cbWrapper.d->cb commit];
+
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+ QRHI_PROF_F(endSwapChainFrame(swapChain, swapChainD->frameCount + 1));
+
+ if (@available(macOS 10.13, iOS 11.0, *))
+ [d->captureScope endScope];
+
+ if (needsPresent)
+ swapChainD->currentFrameSlot = (swapChainD->currentFrameSlot + 1) % QMTL_FRAMES_IN_FLIGHT;
+
+ swapChainD->frameCount += 1;
+ currentSwapChain = nullptr;
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiMetal::beginOffscreenFrame(QRhiCommandBuffer **cb)
+{
+ currentFrameSlot = (currentFrameSlot + 1) % QMTL_FRAMES_IN_FLIGHT;
+ if (swapchains.count() > 1) {
+ for (QMetalSwapChain *sc : qAsConst(swapchains)) {
+ // wait+signal is the general pattern to ensure the commands for a
+ // given frame slot have completed (if sem is 1, we go 0 then 1; if
+ // sem is 0 we go -1, block, completion increments to 0, then us to 1)
+ dispatch_semaphore_t sem = sc->d->sem[currentFrameSlot];
+ dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
+ dispatch_semaphore_signal(sem);
+ }
+ }
+
+ d->ofr.active = true;
+ *cb = &d->ofr.cbWrapper;
+ d->ofr.cbWrapper.d->cb = [d->cmdQueue commandBufferWithUnretainedReferences];
+
+ executeDeferredReleases();
+ d->ofr.cbWrapper.resetState();
+ finishActiveReadbacks();
+
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiMetal::endOffscreenFrame()
+{
+ Q_ASSERT(d->ofr.active);
+ d->ofr.active = false;
+
+ [d->ofr.cbWrapper.d->cb commit];
+
+ // offscreen frames wait for completion, unlike swapchain ones
+ [d->ofr.cbWrapper.d->cb waitUntilCompleted];
+
+ finishActiveReadbacks(true);
+
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiMetal::finish()
+{
+ id<MTLCommandBuffer> cb = nil;
+ QMetalSwapChain *swapChainD = nullptr;
+ if (inFrame) {
+ if (d->ofr.active) {
+ Q_ASSERT(!currentSwapChain);
+ Q_ASSERT(d->ofr.cbWrapper.recordingPass == QMetalCommandBuffer::NoPass);
+ cb = d->ofr.cbWrapper.d->cb;
+ } else {
+ Q_ASSERT(currentSwapChain);
+ swapChainD = currentSwapChain;
+ Q_ASSERT(swapChainD->cbWrapper.recordingPass == QMetalCommandBuffer::NoPass);
+ cb = swapChainD->cbWrapper.d->cb;
+ }
+ }
+
+ for (QMetalSwapChain *sc : qAsConst(swapchains)) {
+ for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
+ if (currentSwapChain && sc == currentSwapChain && i == currentFrameSlot) {
+ // no wait as this is the thing we're going to be commit below and
+ // beginFrame decremented sem already and going to be signaled by endFrame
+ continue;
+ }
+ dispatch_semaphore_t sem = sc->d->sem[i];
+ dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
+ dispatch_semaphore_signal(sem);
+ }
+ }
+
+ if (cb) {
+ [cb commit];
+ [cb waitUntilCompleted];
+ }
+
+ if (inFrame) {
+ if (d->ofr.active)
+ d->ofr.cbWrapper.d->cb = [d->cmdQueue commandBufferWithUnretainedReferences];
+ else
+ swapChainD->cbWrapper.d->cb = [d->cmdQueue commandBufferWithUnretainedReferences];
+ }
+
+ executeDeferredReleases(true);
+
+ finishActiveReadbacks(true);
+
+ return QRhi::FrameOpSuccess;
+}
+
+MTLRenderPassDescriptor *QRhiMetalData::createDefaultRenderPass(bool hasDepthStencil,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ int colorAttCount)
+{
+ MTLRenderPassDescriptor *rp = [MTLRenderPassDescriptor renderPassDescriptor];
+ MTLClearColor c = MTLClearColorMake(colorClearValue.redF(), colorClearValue.greenF(), colorClearValue.blueF(),
+ colorClearValue.alphaF());
+
+ for (int i = 0; i < colorAttCount; ++i) {
+ rp.colorAttachments[i].loadAction = MTLLoadActionClear;
+ rp.colorAttachments[i].storeAction = MTLStoreActionStore;
+ rp.colorAttachments[i].clearColor = c;
+ }
+
+ if (hasDepthStencil) {
+ rp.depthAttachment.loadAction = MTLLoadActionClear;
+ rp.depthAttachment.storeAction = MTLStoreActionDontCare;
+ rp.stencilAttachment.loadAction = MTLLoadActionClear;
+ rp.stencilAttachment.storeAction = MTLStoreActionDontCare;
+ rp.depthAttachment.clearDepth = depthStencilClearValue.depthClearValue();
+ rp.stencilAttachment.clearStencil = depthStencilClearValue.stencilClearValue();
+ }
+
+ return rp;
+}
+
+qsizetype QRhiMetal::subresUploadByteSize(const QRhiTextureSubresourceUploadDescription &subresDesc) const
+{
+ qsizetype size = 0;
+ const qsizetype imageSizeBytes = subresDesc.image().isNull() ?
+ subresDesc.data().size() : subresDesc.image().sizeInBytes();
+ if (imageSizeBytes > 0)
+ size += aligned(imageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
+ return size;
+}
+
+void QRhiMetal::enqueueSubresUpload(QMetalTexture *texD, void *mp, void *blitEncPtr,
+ int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc,
+ qsizetype *curOfs)
+{
+ const QPoint dp = subresDesc.destinationTopLeft();
+ const QByteArray rawData = subresDesc.data();
+ QImage img = subresDesc.image();
+ id<MTLBlitCommandEncoder> blitEnc = (id<MTLBlitCommandEncoder>) blitEncPtr;
+
+ if (!img.isNull()) {
+ const qsizetype fullImageSizeBytes = img.sizeInBytes();
+ int w = img.width();
+ int h = img.height();
+ int bpl = img.bytesPerLine();
+ int srcOffset = 0;
+
+ if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
+ const int sx = subresDesc.sourceTopLeft().x();
+ const int sy = subresDesc.sourceTopLeft().y();
+ if (!subresDesc.sourceSize().isEmpty()) {
+ w = subresDesc.sourceSize().width();
+ h = subresDesc.sourceSize().height();
+ }
+ if (img.depth() == 32) {
+ memcpy(reinterpret_cast<char *>(mp) + *curOfs, img.constBits(), fullImageSizeBytes);
+ srcOffset = sy * bpl + sx * 4;
+ // bpl remains set to the original image's row stride
+ } else {
+ img = img.copy(sx, sy, w, h);
+ bpl = img.bytesPerLine();
+ Q_ASSERT(img.sizeInBytes() <= fullImageSizeBytes);
+ memcpy(reinterpret_cast<char *>(mp) + *curOfs, img.constBits(), img.sizeInBytes());
+ }
+ } else {
+ memcpy(reinterpret_cast<char *>(mp) + *curOfs, img.constBits(), fullImageSizeBytes);
+ }
+
+ [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
+ sourceOffset: *curOfs + srcOffset
+ sourceBytesPerRow: bpl
+ sourceBytesPerImage: 0
+ sourceSize: MTLSizeMake(w, h, 1)
+ toTexture: texD->d->tex
+ destinationSlice: layer
+ destinationLevel: level
+ destinationOrigin: MTLOriginMake(dp.x(), dp.y(), 0)
+ options: MTLBlitOptionNone];
+
+ *curOfs += aligned(fullImageSizeBytes, QRhiMetalData::TEXBUF_ALIGN);
+ } else if (!rawData.isEmpty() && isCompressedFormat(texD->m_format)) {
+ const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
+ const int subresw = subresSize.width();
+ const int subresh = subresSize.height();
+ int w, h;
+ if (subresDesc.sourceSize().isEmpty()) {
+ w = subresw;
+ h = subresh;
+ } else {
+ w = subresDesc.sourceSize().width();
+ h = subresDesc.sourceSize().height();
+ }
+
+ quint32 bpl = 0;
+ QSize blockDim;
+ compressedFormatInfo(texD->m_format, QSize(w, h), &bpl, nullptr, &blockDim);
+
+ const int dx = aligned(dp.x(), blockDim.width());
+ const int dy = aligned(dp.y(), blockDim.height());
+ if (dx + w != subresw)
+ w = aligned(w, blockDim.width());
+ if (dy + h != subresh)
+ h = aligned(h, blockDim.height());
+
+ memcpy(reinterpret_cast<char *>(mp) + *curOfs, rawData.constData(), rawData.size());
+
+ [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
+ sourceOffset: *curOfs
+ sourceBytesPerRow: bpl
+ sourceBytesPerImage: 0
+ sourceSize: MTLSizeMake(w, h, 1)
+ toTexture: texD->d->tex
+ destinationSlice: layer
+ destinationLevel: level
+ destinationOrigin: MTLOriginMake(dx, dy, 0)
+ options: MTLBlitOptionNone];
+
+ *curOfs += aligned(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
+ } else if (!rawData.isEmpty()) {
+ const QSize subresSize = q->sizeForMipLevel(level, texD->m_pixelSize);
+ const int subresw = subresSize.width();
+ const int subresh = subresSize.height();
+ int w, h;
+ if (subresDesc.sourceSize().isEmpty()) {
+ w = subresw;
+ h = subresh;
+ } else {
+ w = subresDesc.sourceSize().width();
+ h = subresDesc.sourceSize().height();
+ }
+
+ quint32 bpl = 0;
+ textureFormatInfo(texD->m_format, QSize(w, h), &bpl, nullptr);
+ memcpy(reinterpret_cast<char *>(mp) + *curOfs, rawData.constData(), rawData.size());
+
+ [blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
+ sourceOffset: *curOfs
+ sourceBytesPerRow: bpl
+ sourceBytesPerImage: 0
+ sourceSize: MTLSizeMake(w, h, 1)
+ toTexture: texD->d->tex
+ destinationSlice: layer
+ destinationLevel: level
+ destinationOrigin: MTLOriginMake(dp.x(), dp.y(), 0)
+ options: MTLBlitOptionNone];
+
+ *curOfs += aligned(rawData.size(), QRhiMetalData::TEXBUF_ALIGN);
+ } else {
+ qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
+ }
+}
+
+void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+
+ for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : ud->dynamicBufferUpdates) {
+ QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, u.buf);
+ Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
+ for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
+ bufD->d->pendingUpdates[i].append(u);
+ }
+
+ // Due to the Metal API the handling of static and dynamic buffers is
+ // basically the same. So go through the same pendingUpdates machinery.
+ for (const QRhiResourceUpdateBatchPrivate::StaticBufferUpload &u : ud->staticBufferUploads) {
+ QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, u.buf);
+ Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
+ Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
+ for (int i = 0, ie = bufD->d->slotted ? QMTL_FRAMES_IN_FLIGHT : 1; i != ie; ++i)
+ bufD->d->pendingUpdates[i].append({ u.buf, u.offset, u.data.size(), u.data.constData() });
+ }
+
+ id<MTLBlitCommandEncoder> blitEnc = nil;
+ auto ensureBlit = [&blitEnc, cbD, this] {
+ if (!blitEnc) {
+ blitEnc = [cbD->d->cb blitCommandEncoder];
+ if (debugMarkers)
+ [blitEnc pushDebugGroup: @"Texture upload/copy"];
+ }
+ };
+
+ for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) {
+ if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
+ QMetalTexture *utexD = QRHI_RES(QMetalTexture, u.upload.tex);
+ qsizetype stagingSize = 0;
+ for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
+ for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
+ for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.upload.subresDesc[layer][level]))
+ stagingSize += subresUploadByteSize(subresDesc);
+ }
+ }
+
+ ensureBlit();
+ Q_ASSERT(!utexD->d->stagingBuf[currentFrameSlot]);
+ utexD->d->stagingBuf[currentFrameSlot] = [d->dev newBufferWithLength: stagingSize
+ options: MTLResourceStorageModeShared];
+ QRHI_PROF_F(newTextureStagingArea(utexD, currentFrameSlot, stagingSize));
+
+ void *mp = [utexD->d->stagingBuf[currentFrameSlot] contents];
+ qsizetype curOfs = 0;
+ for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
+ for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
+ for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.upload.subresDesc[layer][level]))
+ enqueueSubresUpload(utexD, mp, blitEnc, layer, level, subresDesc, &curOfs);
+ }
+ }
+
+ utexD->lastActiveFrameSlot = currentFrameSlot;
+
+ QRhiMetalData::DeferredReleaseEntry e;
+ e.type = QRhiMetalData::DeferredReleaseEntry::StagingBuffer;
+ e.lastActiveFrameSlot = currentFrameSlot;
+ e.stagingBuffer.buffer = utexD->d->stagingBuf[currentFrameSlot];
+ utexD->d->stagingBuf[currentFrameSlot] = nil;
+ d->releaseQueue.append(e);
+ QRHI_PROF_F(releaseTextureStagingArea(utexD, currentFrameSlot));
+ } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) {
+ Q_ASSERT(u.copy.src && u.copy.dst);
+ QMetalTexture *srcD = QRHI_RES(QMetalTexture, u.copy.src);
+ QMetalTexture *dstD = QRHI_RES(QMetalTexture, u.copy.dst);
+ const QPoint dp = u.copy.desc.destinationTopLeft();
+ const QSize size = u.copy.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : u.copy.desc.pixelSize();
+ const QPoint sp = u.copy.desc.sourceTopLeft();
+
+ ensureBlit();
+ [blitEnc copyFromTexture: srcD->d->tex
+ sourceSlice: u.copy.desc.sourceLayer()
+ sourceLevel: u.copy.desc.sourceLevel()
+ sourceOrigin: MTLOriginMake(sp.x(), sp.y(), 0)
+ sourceSize: MTLSizeMake(size.width(), size.height(), 1)
+ toTexture: dstD->d->tex
+ destinationSlice: u.copy.desc.destinationLayer()
+ destinationLevel: u.copy.desc.destinationLevel()
+ destinationOrigin: MTLOriginMake(dp.x(), dp.y(), 0)];
+
+ srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot;
+ } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
+ QRhiMetalData::ActiveReadback aRb;
+ aRb.activeFrameSlot = currentFrameSlot;
+ aRb.desc = u.read.rb;
+ aRb.result = u.read.result;
+
+ QMetalTexture *texD = QRHI_RES(QMetalTexture, u.read.rb.texture());
+ QMetalSwapChain *swapChainD = nullptr;
+ id<MTLTexture> src;
+ QSize srcSize;
+ if (texD) {
+ if (texD->samples > 1) {
+ qWarning("Multisample texture cannot be read back");
+ continue;
+ }
+ aRb.pixelSize = u.read.rb.level() > 0 ? q->sizeForMipLevel(u.read.rb.level(), texD->m_pixelSize)
+ : texD->m_pixelSize;
+ aRb.format = texD->m_format;
+ src = texD->d->tex;
+ srcSize = texD->m_pixelSize;
+ texD->lastActiveFrameSlot = currentFrameSlot;
+ } else {
+ Q_ASSERT(currentSwapChain);
+ swapChainD = QRHI_RES(QMetalSwapChain, currentSwapChain);
+ aRb.pixelSize = swapChainD->pixelSize;
+ aRb.format = swapChainD->d->rhiColorFormat;
+ // Multisample swapchains need nothing special since resolving
+ // happens when ending a renderpass.
+ const QMetalRenderTargetData::ColorAtt &colorAtt(swapChainD->rtWrapper.d->fb.colorAtt[0]);
+ src = colorAtt.resolveTex ? colorAtt.resolveTex : colorAtt.tex;
+ srcSize = swapChainD->rtWrapper.d->pixelSize;
+ }
+
+ quint32 bpl = 0;
+ textureFormatInfo(aRb.format, aRb.pixelSize, &bpl, &aRb.bufSize);
+ aRb.buf = [d->dev newBufferWithLength: aRb.bufSize options: MTLResourceStorageModeShared];
+
+ QRHI_PROF_F(newReadbackBuffer(quint64(quintptr(aRb.buf)),
+ texD ? static_cast<QRhiResource *>(texD) : static_cast<QRhiResource *>(swapChainD),
+ aRb.bufSize));
+
+ ensureBlit();
+ [blitEnc copyFromTexture: src
+ sourceSlice: u.read.rb.layer()
+ sourceLevel: u.read.rb.level()
+ sourceOrigin: MTLOriginMake(0, 0, 0)
+ sourceSize: MTLSizeMake(srcSize.width(), srcSize.height(), 1)
+ toBuffer: aRb.buf
+ destinationOffset: 0
+ destinationBytesPerRow: bpl
+ destinationBytesPerImage: 0
+ options: MTLBlitOptionNone];
+
+ d->activeReadbacks.append(aRb);
+ } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::MipGen) {
+ QMetalTexture *utexD = QRHI_RES(QMetalTexture, u.mipgen.tex);
+ ensureBlit();
+ [blitEnc generateMipmapsForTexture: utexD->d->tex];
+ utexD->lastActiveFrameSlot = currentFrameSlot;
+ }
+ }
+
+ if (blitEnc) {
+ if (debugMarkers)
+ [blitEnc popDebugGroup];
+ [blitEnc endEncoding];
+ }
+
+ ud->free();
+}
+
+// this handles all types of buffers, not just Dynamic
+void QRhiMetal::executeBufferHostWritesForCurrentFrame(QMetalBuffer *bufD)
+{
+ const int idx = bufD->d->slotted ? currentFrameSlot : 0;
+ QVector<QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate> &updates(bufD->d->pendingUpdates[idx]);
+ if (updates.isEmpty())
+ return;
+
+ void *p = [bufD->d->buf[idx] contents];
+ int changeBegin = -1;
+ int changeEnd = -1;
+ for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : updates) {
+ Q_ASSERT(bufD == QRHI_RES(QMetalBuffer, u.buf));
+ memcpy(static_cast<char *>(p) + u.offset, u.data.constData(), u.data.size());
+ if (changeBegin == -1 || u.offset < changeBegin)
+ changeBegin = u.offset;
+ if (changeEnd == -1 || u.offset + u.data.size() > changeEnd)
+ changeEnd = u.offset + u.data.size();
+ }
+ if (changeBegin >= 0 && bufD->d->managed)
+ [bufD->d->buf[idx] didModifyRange: NSMakeRange(changeBegin, changeEnd - changeBegin)];
+
+ updates.clear();
+}
+
+void QRhiMetal::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_ASSERT(QRHI_RES(QMetalCommandBuffer, cb)->recordingPass == QMetalCommandBuffer::NoPass);
+
+ enqueueResourceUpdates(cb, resourceUpdates);
+}
+
+void QRhiMetal::beginPass(QRhiCommandBuffer *cb,
+ QRhiRenderTarget *rt,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ QRhiResourceUpdateBatch *resourceUpdates)
+{
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::NoPass);
+
+ if (resourceUpdates)
+ enqueueResourceUpdates(cb, resourceUpdates);
+
+ QMetalRenderTargetData *rtD = nullptr;
+ switch (rt->resourceType()) {
+ case QRhiResource::RenderTarget:
+ rtD = QRHI_RES(QMetalReferenceRenderTarget, rt)->d;
+ cbD->d->currentPassRpDesc = d->createDefaultRenderPass(rtD->dsAttCount, colorClearValue, depthStencilClearValue, rtD->colorAttCount);
+ if (rtD->colorAttCount) {
+ QMetalRenderTargetData::ColorAtt &color0(rtD->fb.colorAtt[0]);
+ if (color0.needsDrawableForTex || color0.needsDrawableForResolveTex) {
+ Q_ASSERT(currentSwapChain);
+ QMetalSwapChain *swapChainD = QRHI_RES(QMetalSwapChain, currentSwapChain);
+ if (!swapChainD->d->curDrawable)
+ swapChainD->d->curDrawable = [swapChainD->d->layer nextDrawable];
+ if (!swapChainD->d->curDrawable) {
+ qWarning("No drawable");
+ return;
+ }
+ id<MTLTexture> scTex = swapChainD->d->curDrawable.texture;
+ if (color0.needsDrawableForTex) {
+ color0.tex = scTex;
+ color0.needsDrawableForTex = false;
+ } else {
+ color0.resolveTex = scTex;
+ color0.needsDrawableForResolveTex = false;
+ }
+ }
+ }
+ break;
+ case QRhiResource::TextureRenderTarget:
+ {
+ QMetalTextureRenderTarget *rtTex = QRHI_RES(QMetalTextureRenderTarget, rt);
+ rtD = rtTex->d;
+ cbD->d->currentPassRpDesc = d->createDefaultRenderPass(rtD->dsAttCount, colorClearValue, depthStencilClearValue, rtD->colorAttCount);
+ if (rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents)) {
+ for (int i = 0; i < rtD->colorAttCount; ++i)
+ cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
+ }
+ if (rtD->dsAttCount && rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents)) {
+ cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
+ cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
+ }
+ const QVector<QRhiColorAttachment> colorAttachments = rtTex->m_desc.colorAttachments();
+ for (const QRhiColorAttachment &colorAttachment : colorAttachments) {
+ if (colorAttachment.texture())
+ QRHI_RES(QMetalTexture, colorAttachment.texture())->lastActiveFrameSlot = currentFrameSlot;
+ else if (colorAttachment.renderBuffer())
+ QRHI_RES(QMetalRenderBuffer, colorAttachment.renderBuffer())->lastActiveFrameSlot = currentFrameSlot;
+ if (colorAttachment.resolveTexture())
+ QRHI_RES(QMetalTexture, colorAttachment.resolveTexture())->lastActiveFrameSlot = currentFrameSlot;
+ }
+ if (rtTex->m_desc.depthStencilBuffer())
+ QRHI_RES(QMetalRenderBuffer, rtTex->m_desc.depthStencilBuffer())->lastActiveFrameSlot = currentFrameSlot;
+ if (rtTex->m_desc.depthTexture())
+ QRHI_RES(QMetalTexture, rtTex->m_desc.depthTexture())->lastActiveFrameSlot = currentFrameSlot;
+ }
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ for (int i = 0; i < rtD->colorAttCount; ++i) {
+ cbD->d->currentPassRpDesc.colorAttachments[i].texture = rtD->fb.colorAtt[i].tex;
+ cbD->d->currentPassRpDesc.colorAttachments[i].slice = rtD->fb.colorAtt[i].layer;
+ cbD->d->currentPassRpDesc.colorAttachments[i].level = rtD->fb.colorAtt[i].level;
+ if (rtD->fb.colorAtt[i].resolveTex) {
+ cbD->d->currentPassRpDesc.colorAttachments[i].storeAction = MTLStoreActionMultisampleResolve;
+ cbD->d->currentPassRpDesc.colorAttachments[i].resolveTexture = rtD->fb.colorAtt[i].resolveTex;
+ cbD->d->currentPassRpDesc.colorAttachments[i].resolveSlice = rtD->fb.colorAtt[i].resolveLayer;
+ cbD->d->currentPassRpDesc.colorAttachments[i].resolveLevel = rtD->fb.colorAtt[i].resolveLevel;
+ }
+ }
+
+ if (rtD->dsAttCount) {
+ Q_ASSERT(rtD->fb.dsTex);
+ cbD->d->currentPassRpDesc.depthAttachment.texture = rtD->fb.dsTex;
+ cbD->d->currentPassRpDesc.stencilAttachment.texture = rtD->fb.hasStencil ? rtD->fb.dsTex : nil;
+ if (rtD->fb.depthNeedsStore) // Depth/Stencil is set to DontCare by default, override if needed
+ cbD->d->currentPassRpDesc.depthAttachment.storeAction = MTLStoreActionStore;
+ }
+
+ cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
+
+ cbD->resetPerPassState();
+
+ cbD->recordingPass = QMetalCommandBuffer::RenderPass;
+ cbD->currentTarget = rt;
+}
+
+void QRhiMetal::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::RenderPass);
+
+ [cbD->d->currentRenderPassEncoder endEncoding];
+
+ cbD->recordingPass = QMetalCommandBuffer::NoPass;
+ cbD->currentTarget = nullptr;
+
+ if (resourceUpdates)
+ enqueueResourceUpdates(cb, resourceUpdates);
+}
+
+void QRhiMetal::beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::NoPass);
+
+ if (resourceUpdates)
+ enqueueResourceUpdates(cb, resourceUpdates);
+
+ cbD->d->currentComputePassEncoder = [cbD->d->cb computeCommandEncoder];
+ cbD->resetPerPassState();
+ cbD->recordingPass = QMetalCommandBuffer::ComputePass;
+}
+
+void QRhiMetal::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::ComputePass);
+
+ [cbD->d->currentComputePassEncoder endEncoding];
+ cbD->recordingPass = QMetalCommandBuffer::NoPass;
+
+ if (resourceUpdates)
+ enqueueResourceUpdates(cb, resourceUpdates);
+}
+
+void QRhiMetal::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps)
+{
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::ComputePass);
+ QMetalComputePipeline *psD = QRHI_RES(QMetalComputePipeline, ps);
+
+ if (cbD->currentComputePipeline != ps || cbD->currentPipelineGeneration != psD->generation) {
+ cbD->currentGraphicsPipeline = nullptr;
+ cbD->currentComputePipeline = ps;
+ cbD->currentPipelineGeneration = psD->generation;
+
+ [cbD->d->currentComputePassEncoder setComputePipelineState: psD->d->ps];
+ }
+
+ psD->lastActiveFrameSlot = currentFrameSlot;
+}
+
+void QRhiMetal::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
+{
+ QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QMetalCommandBuffer::ComputePass);
+ QMetalComputePipeline *psD = QRHI_RES(QMetalComputePipeline, cbD->currentComputePipeline);
+
+ [cbD->d->currentComputePassEncoder dispatchThreadgroups: MTLSizeMake(x, y, z)
+ threadsPerThreadgroup: psD->d->localSize];
+}
+
+static void qrhimtl_releaseBuffer(const QRhiMetalData::DeferredReleaseEntry &e)
+{
+ for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
+ [e.buffer.buffers[i] release];
+}
+
+static void qrhimtl_releaseRenderBuffer(const QRhiMetalData::DeferredReleaseEntry &e)
+{
+ [e.renderbuffer.texture release];
+}
+
+static void qrhimtl_releaseTexture(const QRhiMetalData::DeferredReleaseEntry &e)
+{
+ [e.texture.texture release];
+ for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
+ [e.texture.stagingBuffers[i] release];
+ for (int i = 0; i < QRhi::MAX_LEVELS; ++i)
+ [e.texture.views[i] release];
+}
+
+static void qrhimtl_releaseSampler(const QRhiMetalData::DeferredReleaseEntry &e)
+{
+ [e.sampler.samplerState release];
+}
+
+void QRhiMetal::executeDeferredReleases(bool forced)
+{
+ for (int i = d->releaseQueue.count() - 1; i >= 0; --i) {
+ const QRhiMetalData::DeferredReleaseEntry &e(d->releaseQueue[i]);
+ if (forced || currentFrameSlot == e.lastActiveFrameSlot || e.lastActiveFrameSlot < 0) {
+ switch (e.type) {
+ case QRhiMetalData::DeferredReleaseEntry::Buffer:
+ qrhimtl_releaseBuffer(e);
+ break;
+ case QRhiMetalData::DeferredReleaseEntry::RenderBuffer:
+ qrhimtl_releaseRenderBuffer(e);
+ break;
+ case QRhiMetalData::DeferredReleaseEntry::Texture:
+ qrhimtl_releaseTexture(e);
+ break;
+ case QRhiMetalData::DeferredReleaseEntry::Sampler:
+ qrhimtl_releaseSampler(e);
+ break;
+ case QRhiMetalData::DeferredReleaseEntry::StagingBuffer:
+ [e.stagingBuffer.buffer release];
+ break;
+ default:
+ break;
+ }
+ d->releaseQueue.removeAt(i);
+ }
+ }
+}
+
+void QRhiMetal::finishActiveReadbacks(bool forced)
+{
+ QVarLengthArray<std::function<void()>, 4> completedCallbacks;
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+
+ for (int i = d->activeReadbacks.count() - 1; i >= 0; --i) {
+ const QRhiMetalData::ActiveReadback &aRb(d->activeReadbacks[i]);
+ if (forced || currentFrameSlot == aRb.activeFrameSlot || aRb.activeFrameSlot < 0) {
+ aRb.result->format = aRb.format;
+ aRb.result->pixelSize = aRb.pixelSize;
+ aRb.result->data.resize(aRb.bufSize);
+ void *p = [aRb.buf contents];
+ memcpy(aRb.result->data.data(), p, aRb.bufSize);
+ [aRb.buf release];
+
+ QRHI_PROF_F(releaseReadbackBuffer(quint64(quintptr(aRb.buf))));
+
+ if (aRb.result->completed)
+ completedCallbacks.append(aRb.result->completed);
+
+ d->activeReadbacks.removeAt(i);
+ }
+ }
+
+ for (auto f : completedCallbacks)
+ f();
+}
+
+QMetalBuffer::QMetalBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size)
+ : QRhiBuffer(rhi, type, usage, size),
+ d(new QMetalBufferData)
+{
+ for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
+ d->buf[i] = nil;
+}
+
+QMetalBuffer::~QMetalBuffer()
+{
+ release();
+ delete d;
+}
+
+void QMetalBuffer::release()
+{
+ if (!d->buf[0])
+ return;
+
+ QRhiMetalData::DeferredReleaseEntry e;
+ e.type = QRhiMetalData::DeferredReleaseEntry::Buffer;
+ e.lastActiveFrameSlot = lastActiveFrameSlot;
+
+ for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
+ e.buffer.buffers[i] = d->buf[i];
+ d->buf[i] = nil;
+ d->pendingUpdates[i].clear();
+ }
+
+ QRHI_RES_RHI(QRhiMetal);
+ rhiD->d->releaseQueue.append(e);
+ QRHI_PROF;
+ QRHI_PROF_F(releaseBuffer(this));
+ rhiD->unregisterResource(this);
+}
+
+bool QMetalBuffer::build()
+{
+ if (d->buf[0])
+ release();
+
+ if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) {
+ qWarning("StorageBuffer cannot be combined with Dynamic");
+ return false;
+ }
+
+ const int nonZeroSize = m_size <= 0 ? 256 : m_size;
+ const int roundedSize = m_usage.testFlag(QRhiBuffer::UniformBuffer) ? aligned(nonZeroSize, 256) : nonZeroSize;
+
+ d->managed = false;
+ MTLResourceOptions opts = MTLResourceStorageModeShared;
+#ifdef Q_OS_MACOS
+ if (m_type != Dynamic) {
+ opts = MTLResourceStorageModeManaged;
+ d->managed = true;
+ }
+#endif
+
+ // Immutable and Static only has buf[0] and pendingUpdates[0] in use.
+ // Dynamic uses all.
+ d->slotted = m_type == Dynamic;
+
+ QRHI_RES_RHI(QRhiMetal);
+ for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
+ if (i == 0 || d->slotted) {
+ d->buf[i] = [rhiD->d->dev newBufferWithLength: roundedSize options: opts];
+ d->pendingUpdates[i].reserve(16);
+ if (!m_objectName.isEmpty()) {
+ if (!d->slotted) {
+ d->buf[i].label = [NSString stringWithUTF8String: m_objectName.constData()];
+ } else {
+ const QByteArray name = m_objectName + '/' + QByteArray::number(i);
+ d->buf[i].label = [NSString stringWithUTF8String: name.constData()];
+ }
+ }
+ }
+ }
+
+ QRHI_PROF;
+ QRHI_PROF_F(newBuffer(this, roundedSize, d->slotted ? QMTL_FRAMES_IN_FLIGHT : 1, 0));
+
+ lastActiveFrameSlot = -1;
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+QMetalRenderBuffer::QMetalRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags)
+ : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags),
+ d(new QMetalRenderBufferData)
+{
+}
+
+QMetalRenderBuffer::~QMetalRenderBuffer()
+{
+ release();
+ delete d;
+}
+
+void QMetalRenderBuffer::release()
+{
+ if (!d->tex)
+ return;
+
+ QRhiMetalData::DeferredReleaseEntry e;
+ e.type = QRhiMetalData::DeferredReleaseEntry::RenderBuffer;
+ e.lastActiveFrameSlot = lastActiveFrameSlot;
+
+ e.renderbuffer.texture = d->tex;
+ d->tex = nil;
+
+ QRHI_RES_RHI(QRhiMetal);
+ rhiD->d->releaseQueue.append(e);
+ QRHI_PROF;
+ QRHI_PROF_F(releaseRenderBuffer(this));
+ rhiD->unregisterResource(this);
+}
+
+bool QMetalRenderBuffer::build()
+{
+ if (d->tex)
+ release();
+
+ if (m_pixelSize.isEmpty())
+ return false;
+
+ QRHI_RES_RHI(QRhiMetal);
+ samples = rhiD->effectiveSampleCount(m_sampleCount);
+
+ MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
+ desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
+ desc.width = m_pixelSize.width();
+ desc.height = m_pixelSize.height();
+ if (samples > 1)
+ desc.sampleCount = samples;
+ desc.resourceOptions = MTLResourceStorageModePrivate;
+ desc.usage = MTLTextureUsageRenderTarget;
+
+ bool transientBacking = false;
+ switch (m_type) {
+ case DepthStencil:
+#ifdef Q_OS_MACOS
+ desc.storageMode = MTLStorageModePrivate;
+#else
+ desc.storageMode = MTLResourceStorageModeMemoryless;
+ transientBacking = true;
+#endif
+ d->format = rhiD->d->dev.depth24Stencil8PixelFormatSupported
+ ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
+ desc.pixelFormat = d->format;
+ break;
+ case Color:
+ desc.storageMode = MTLStorageModePrivate;
+ d->format = MTLPixelFormatRGBA8Unorm;
+ desc.pixelFormat = d->format;
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
+ [desc release];
+
+ if (!m_objectName.isEmpty())
+ d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
+
+ QRHI_PROF;
+ QRHI_PROF_F(newRenderBuffer(this, transientBacking, false, samples));
+
+ lastActiveFrameSlot = -1;
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+QRhiTexture::Format QMetalRenderBuffer::backingFormat() const
+{
+ return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
+}
+
+QMetalTexture::QMetalTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+ int sampleCount, Flags flags)
+ : QRhiTexture(rhi, format, pixelSize, sampleCount, flags),
+ d(new QMetalTextureData(this))
+{
+ for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i)
+ d->stagingBuf[i] = nil;
+
+ for (int i = 0; i < QRhi::MAX_LEVELS; ++i)
+ d->perLevelViews[i] = nil;
+}
+
+QMetalTexture::~QMetalTexture()
+{
+ release();
+ delete d;
+}
+
+void QMetalTexture::release()
+{
+ if (!d->tex)
+ return;
+
+ QRhiMetalData::DeferredReleaseEntry e;
+ e.type = QRhiMetalData::DeferredReleaseEntry::Texture;
+ e.lastActiveFrameSlot = lastActiveFrameSlot;
+
+ e.texture.texture = d->owns ? d->tex : nil;
+ d->tex = nil;
+ nativeHandlesStruct.texture = nullptr;
+
+ for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
+ e.texture.stagingBuffers[i] = d->stagingBuf[i];
+ d->stagingBuf[i] = nil;
+ }
+
+ for (int i = 0; i < QRhi::MAX_LEVELS; ++i) {
+ e.texture.views[i] = d->perLevelViews[i];
+ d->perLevelViews[i] = nil;
+ }
+
+ QRHI_RES_RHI(QRhiMetal);
+ rhiD->d->releaseQueue.append(e);
+ QRHI_PROF;
+ QRHI_PROF_F(releaseTexture(this));
+ rhiD->unregisterResource(this);
+}
+
+static inline MTLPixelFormat toMetalTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags)
+{
+ const bool srgb = flags.testFlag(QRhiTexture::sRGB);
+ switch (format) {
+ case QRhiTexture::RGBA8:
+ return srgb ? MTLPixelFormatRGBA8Unorm_sRGB : MTLPixelFormatRGBA8Unorm;
+ case QRhiTexture::BGRA8:
+ return srgb ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
+ case QRhiTexture::R8:
+#ifdef Q_OS_MACOS
+ return MTLPixelFormatR8Unorm;
+#else
+ return srgb ? MTLPixelFormatR8Unorm_sRGB : MTLPixelFormatR8Unorm;
+#endif
+ case QRhiTexture::R16:
+ return MTLPixelFormatR16Unorm;
+ case QRhiTexture::RED_OR_ALPHA8:
+ return MTLPixelFormatR8Unorm;
+
+ case QRhiTexture::RGBA16F:
+ return MTLPixelFormatRGBA16Float;
+ case QRhiTexture::RGBA32F:
+ return MTLPixelFormatRGBA32Float;
+
+ case QRhiTexture::D16:
+#ifdef Q_OS_MACOS
+ return MTLPixelFormatDepth16Unorm;
+#else
+ return MTLPixelFormatDepth32Float;
+#endif
+ case QRhiTexture::D32F:
+ return MTLPixelFormatDepth32Float;
+
+#ifdef Q_OS_MACOS
+ case QRhiTexture::BC1:
+ return srgb ? MTLPixelFormatBC1_RGBA_sRGB : MTLPixelFormatBC1_RGBA;
+ case QRhiTexture::BC2:
+ return srgb ? MTLPixelFormatBC2_RGBA_sRGB : MTLPixelFormatBC2_RGBA;
+ case QRhiTexture::BC3:
+ return srgb ? MTLPixelFormatBC3_RGBA_sRGB : MTLPixelFormatBC3_RGBA;
+ case QRhiTexture::BC4:
+ return MTLPixelFormatBC4_RUnorm;
+ case QRhiTexture::BC5:
+ qWarning("QRhiMetal does not support BC5");
+ return MTLPixelFormatRGBA8Unorm;
+ case QRhiTexture::BC6H:
+ return MTLPixelFormatBC6H_RGBUfloat;
+ case QRhiTexture::BC7:
+ return srgb ? MTLPixelFormatBC7_RGBAUnorm_sRGB : MTLPixelFormatBC7_RGBAUnorm;
+#else
+ case QRhiTexture::BC1:
+ case QRhiTexture::BC2:
+ case QRhiTexture::BC3:
+ case QRhiTexture::BC4:
+ case QRhiTexture::BC5:
+ case QRhiTexture::BC6H:
+ case QRhiTexture::BC7:
+ qWarning("QRhiMetal: BCx compression not supported on this platform");
+ return MTLPixelFormatRGBA8Unorm;
+#endif
+
+#ifndef Q_OS_MACOS
+ case QRhiTexture::ETC2_RGB8:
+ return srgb ? MTLPixelFormatETC2_RGB8_sRGB : MTLPixelFormatETC2_RGB8;
+ case QRhiTexture::ETC2_RGB8A1:
+ return srgb ? MTLPixelFormatETC2_RGB8A1_sRGB : MTLPixelFormatETC2_RGB8A1;
+ case QRhiTexture::ETC2_RGBA8:
+ return srgb ? MTLPixelFormatEAC_RGBA8_sRGB : MTLPixelFormatEAC_RGBA8;
+
+ case QRhiTexture::ASTC_4x4:
+ return srgb ? MTLPixelFormatASTC_4x4_sRGB : MTLPixelFormatASTC_4x4_LDR;
+ case QRhiTexture::ASTC_5x4:
+ return srgb ? MTLPixelFormatASTC_5x4_sRGB : MTLPixelFormatASTC_5x4_LDR;
+ case QRhiTexture::ASTC_5x5:
+ return srgb ? MTLPixelFormatASTC_5x5_sRGB : MTLPixelFormatASTC_5x5_LDR;
+ case QRhiTexture::ASTC_6x5:
+ return srgb ? MTLPixelFormatASTC_6x5_sRGB : MTLPixelFormatASTC_6x5_LDR;
+ case QRhiTexture::ASTC_6x6:
+ return srgb ? MTLPixelFormatASTC_6x6_sRGB : MTLPixelFormatASTC_6x6_LDR;
+ case QRhiTexture::ASTC_8x5:
+ return srgb ? MTLPixelFormatASTC_8x5_sRGB : MTLPixelFormatASTC_8x5_LDR;
+ case QRhiTexture::ASTC_8x6:
+ return srgb ? MTLPixelFormatASTC_8x6_sRGB : MTLPixelFormatASTC_8x6_LDR;
+ case QRhiTexture::ASTC_8x8:
+ return srgb ? MTLPixelFormatASTC_8x8_sRGB : MTLPixelFormatASTC_8x8_LDR;
+ case QRhiTexture::ASTC_10x5:
+ return srgb ? MTLPixelFormatASTC_10x5_sRGB : MTLPixelFormatASTC_10x5_LDR;
+ case QRhiTexture::ASTC_10x6:
+ return srgb ? MTLPixelFormatASTC_10x6_sRGB : MTLPixelFormatASTC_10x6_LDR;
+ case QRhiTexture::ASTC_10x8:
+ return srgb ? MTLPixelFormatASTC_10x8_sRGB : MTLPixelFormatASTC_10x8_LDR;
+ case QRhiTexture::ASTC_10x10:
+ return srgb ? MTLPixelFormatASTC_10x10_sRGB : MTLPixelFormatASTC_10x10_LDR;
+ case QRhiTexture::ASTC_12x10:
+ return srgb ? MTLPixelFormatASTC_12x10_sRGB : MTLPixelFormatASTC_12x10_LDR;
+ case QRhiTexture::ASTC_12x12:
+ return srgb ? MTLPixelFormatASTC_12x12_sRGB : MTLPixelFormatASTC_12x12_LDR;
+#else
+ case QRhiTexture::ETC2_RGB8:
+ case QRhiTexture::ETC2_RGB8A1:
+ case QRhiTexture::ETC2_RGBA8:
+ qWarning("QRhiMetal: ETC2 compression not supported on this platform");
+ return MTLPixelFormatRGBA8Unorm;
+
+ case QRhiTexture::ASTC_4x4:
+ case QRhiTexture::ASTC_5x4:
+ case QRhiTexture::ASTC_5x5:
+ case QRhiTexture::ASTC_6x5:
+ case QRhiTexture::ASTC_6x6:
+ case QRhiTexture::ASTC_8x5:
+ case QRhiTexture::ASTC_8x6:
+ case QRhiTexture::ASTC_8x8:
+ case QRhiTexture::ASTC_10x5:
+ case QRhiTexture::ASTC_10x6:
+ case QRhiTexture::ASTC_10x8:
+ case QRhiTexture::ASTC_10x10:
+ case QRhiTexture::ASTC_12x10:
+ case QRhiTexture::ASTC_12x12:
+ qWarning("QRhiMetal: ASTC compression not supported on this platform");
+ return MTLPixelFormatRGBA8Unorm;
+#endif
+
+ default:
+ Q_UNREACHABLE();
+ return MTLPixelFormatRGBA8Unorm;
+ }
+}
+
+bool QMetalTexture::prepareBuild(QSize *adjustedSize)
+{
+ if (d->tex)
+ release();
+
+ const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
+ const bool isCube = m_flags.testFlag(CubeMap);
+ const bool hasMipMaps = m_flags.testFlag(MipMapped);
+
+ QRHI_RES_RHI(QRhiMetal);
+ d->format = toMetalTextureFormat(m_format, m_flags);
+ mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
+ samples = rhiD->effectiveSampleCount(m_sampleCount);
+ if (samples > 1) {
+ if (isCube) {
+ qWarning("Cubemap texture cannot be multisample");
+ return false;
+ }
+ if (hasMipMaps) {
+ qWarning("Multisample texture cannot have mipmaps");
+ return false;
+ }
+ }
+
+ if (adjustedSize)
+ *adjustedSize = size;
+
+ return true;
+}
+
+bool QMetalTexture::build()
+{
+ QSize size;
+ if (!prepareBuild(&size))
+ return false;
+
+ MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
+
+ const bool isCube = m_flags.testFlag(CubeMap);
+ if (isCube)
+ desc.textureType = MTLTextureTypeCube;
+ else
+ desc.textureType = samples > 1 ? MTLTextureType2DMultisample : MTLTextureType2D;
+ desc.pixelFormat = d->format;
+ desc.width = size.width();
+ desc.height = size.height();
+ desc.mipmapLevelCount = mipLevelCount;
+ if (samples > 1)
+ desc.sampleCount = samples;
+ desc.resourceOptions = MTLResourceStorageModePrivate;
+ desc.storageMode = MTLStorageModePrivate;
+ desc.usage = MTLTextureUsageShaderRead;
+ if (m_flags.testFlag(RenderTarget))
+ desc.usage |= MTLTextureUsageRenderTarget;
+ if (m_flags.testFlag(UsedWithLoadStore))
+ desc.usage |= MTLTextureUsageShaderWrite;
+
+ QRHI_RES_RHI(QRhiMetal);
+ d->tex = [rhiD->d->dev newTextureWithDescriptor: desc];
+ [desc release];
+
+ if (!m_objectName.isEmpty())
+ d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
+
+ d->owns = true;
+ nativeHandlesStruct.texture = d->tex;
+
+ QRHI_PROF;
+ QRHI_PROF_F(newTexture(this, true, mipLevelCount, isCube ? 6 : 1, samples));
+
+ lastActiveFrameSlot = -1;
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+bool QMetalTexture::buildFrom(const QRhiNativeHandles *src)
+{
+ const QRhiMetalTextureNativeHandles *h = static_cast<const QRhiMetalTextureNativeHandles *>(src);
+ if (!h || !h->texture)
+ return false;
+
+ if (!prepareBuild())
+ return false;
+
+ d->tex = (id<MTLTexture>) h->texture;
+
+ d->owns = false;
+ nativeHandlesStruct.texture = d->tex;
+
+ QRHI_PROF;
+ QRHI_PROF_F(newTexture(this, false, mipLevelCount, m_flags.testFlag(CubeMap) ? 6 : 1, samples));
+
+ lastActiveFrameSlot = -1;
+ generation += 1;
+ QRHI_RES_RHI(QRhiMetal);
+ rhiD->registerResource(this);
+ return true;
+}
+
+const QRhiNativeHandles *QMetalTexture::nativeHandles()
+{
+ return &nativeHandlesStruct;
+}
+
+id<MTLTexture> QMetalTextureData::viewForLevel(int level)
+{
+ Q_ASSERT(level >= 0 && level < int(q->mipLevelCount));
+ if (perLevelViews[level])
+ return perLevelViews[level];
+
+ const MTLTextureType type = [tex textureType];
+ const bool isCube = q->m_flags.testFlag(QRhiTexture::CubeMap);
+ id<MTLTexture> view = [tex newTextureViewWithPixelFormat: format textureType: type
+ levels: NSMakeRange(level, 1) slices: NSMakeRange(0, isCube ? 6 : 1)];
+
+ perLevelViews[level] = view;
+ return view;
+}
+
+QMetalSampler::QMetalSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
+ AddressMode u, AddressMode v)
+ : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v),
+ d(new QMetalSamplerData)
+{
+}
+
+QMetalSampler::~QMetalSampler()
+{
+ release();
+ delete d;
+}
+
+void QMetalSampler::release()
+{
+ if (!d->samplerState)
+ return;
+
+ QRhiMetalData::DeferredReleaseEntry e;
+ e.type = QRhiMetalData::DeferredReleaseEntry::Sampler;
+ e.lastActiveFrameSlot = lastActiveFrameSlot;
+
+ e.sampler.samplerState = d->samplerState;
+ d->samplerState = nil;
+
+ QRHI_RES_RHI(QRhiMetal);
+ rhiD->d->releaseQueue.append(e);
+ rhiD->unregisterResource(this);
+}
+
+static inline MTLSamplerMinMagFilter toMetalFilter(QRhiSampler::Filter f)
+{
+ switch (f) {
+ case QRhiSampler::Nearest:
+ return MTLSamplerMinMagFilterNearest;
+ case QRhiSampler::Linear:
+ return MTLSamplerMinMagFilterLinear;
+ default:
+ Q_UNREACHABLE();
+ return MTLSamplerMinMagFilterNearest;
+ }
+}
+
+static inline MTLSamplerMipFilter toMetalMipmapMode(QRhiSampler::Filter f)
+{
+ switch (f) {
+ case QRhiSampler::None:
+ return MTLSamplerMipFilterNotMipmapped;
+ case QRhiSampler::Nearest:
+ return MTLSamplerMipFilterNearest;
+ case QRhiSampler::Linear:
+ return MTLSamplerMipFilterLinear;
+ default:
+ Q_UNREACHABLE();
+ return MTLSamplerMipFilterNotMipmapped;
+ }
+}
+
+static inline MTLSamplerAddressMode toMetalAddressMode(QRhiSampler::AddressMode m)
+{
+ switch (m) {
+ case QRhiSampler::Repeat:
+ return MTLSamplerAddressModeRepeat;
+ case QRhiSampler::ClampToEdge:
+ return MTLSamplerAddressModeClampToEdge;
+ case QRhiSampler::Border:
+ return MTLSamplerAddressModeClampToBorderColor;
+ case QRhiSampler::Mirror:
+ return MTLSamplerAddressModeMirrorRepeat;
+ case QRhiSampler::MirrorOnce:
+ return MTLSamplerAddressModeMirrorClampToEdge;
+ default:
+ Q_UNREACHABLE();
+ return MTLSamplerAddressModeClampToEdge;
+ }
+}
+
+static inline MTLCompareFunction toMetalTextureCompareFunction(QRhiSampler::CompareOp op)
+{
+ switch (op) {
+ case QRhiSampler::Never:
+ return MTLCompareFunctionNever;
+ case QRhiSampler::Less:
+ return MTLCompareFunctionLess;
+ case QRhiSampler::Equal:
+ return MTLCompareFunctionEqual;
+ case QRhiSampler::LessOrEqual:
+ return MTLCompareFunctionLessEqual;
+ case QRhiSampler::Greater:
+ return MTLCompareFunctionGreater;
+ case QRhiSampler::NotEqual:
+ return MTLCompareFunctionNotEqual;
+ case QRhiSampler::GreaterOrEqual:
+ return MTLCompareFunctionGreaterEqual;
+ case QRhiSampler::Always:
+ return MTLCompareFunctionAlways;
+ default:
+ Q_UNREACHABLE();
+ return MTLCompareFunctionNever;
+ }
+}
+
+bool QMetalSampler::build()
+{
+ if (d->samplerState)
+ release();
+
+ MTLSamplerDescriptor *desc = [[MTLSamplerDescriptor alloc] init];
+ desc.minFilter = toMetalFilter(m_minFilter);
+ desc.magFilter = toMetalFilter(m_magFilter);
+ desc.mipFilter = toMetalMipmapMode(m_mipmapMode);
+ desc.sAddressMode = toMetalAddressMode(m_addressU);
+ desc.tAddressMode = toMetalAddressMode(m_addressV);
+ desc.rAddressMode = toMetalAddressMode(m_addressW);
+ desc.compareFunction = toMetalTextureCompareFunction(m_compareOp);
+
+ QRHI_RES_RHI(QRhiMetal);
+ d->samplerState = [rhiD->d->dev newSamplerStateWithDescriptor: desc];
+ [desc release];
+
+ lastActiveFrameSlot = -1;
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+// dummy, no Vulkan-style RenderPass+Framebuffer concept here.
+// We do have MTLRenderPassDescriptor of course, but it will be created on the fly for each pass.
+QMetalRenderPassDescriptor::QMetalRenderPassDescriptor(QRhiImplementation *rhi)
+ : QRhiRenderPassDescriptor(rhi)
+{
+}
+
+QMetalRenderPassDescriptor::~QMetalRenderPassDescriptor()
+{
+ release();
+}
+
+void QMetalRenderPassDescriptor::release()
+{
+ // nothing to do here
+}
+
+QMetalReferenceRenderTarget::QMetalReferenceRenderTarget(QRhiImplementation *rhi)
+ : QRhiRenderTarget(rhi),
+ d(new QMetalRenderTargetData)
+{
+}
+
+QMetalReferenceRenderTarget::~QMetalReferenceRenderTarget()
+{
+ release();
+ delete d;
+}
+
+void QMetalReferenceRenderTarget::release()
+{
+ // nothing to do here
+}
+
+QSize QMetalReferenceRenderTarget::pixelSize() const
+{
+ return d->pixelSize;
+}
+
+float QMetalReferenceRenderTarget::devicePixelRatio() const
+{
+ return d->dpr;
+}
+
+int QMetalReferenceRenderTarget::sampleCount() const
+{
+ return d->sampleCount;
+}
+
+QMetalTextureRenderTarget::QMetalTextureRenderTarget(QRhiImplementation *rhi,
+ const QRhiTextureRenderTargetDescription &desc,
+ Flags flags)
+ : QRhiTextureRenderTarget(rhi, desc, flags),
+ d(new QMetalRenderTargetData)
+{
+}
+
+QMetalTextureRenderTarget::~QMetalTextureRenderTarget()
+{
+ release();
+ delete d;
+}
+
+void QMetalTextureRenderTarget::release()
+{
+ // nothing to do here
+}
+
+QRhiRenderPassDescriptor *QMetalTextureRenderTarget::newCompatibleRenderPassDescriptor()
+{
+ const QVector<QRhiColorAttachment> colorAttachments = m_desc.colorAttachments();
+ QMetalRenderPassDescriptor *rpD = new QMetalRenderPassDescriptor(m_rhi);
+ rpD->colorAttachmentCount = colorAttachments.count();
+ rpD->hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
+
+ for (int i = 0, ie = colorAttachments.count(); i != ie; ++i) {
+ QMetalTexture *texD = QRHI_RES(QMetalTexture, colorAttachments[i].texture());
+ QMetalRenderBuffer *rbD = QRHI_RES(QMetalRenderBuffer, colorAttachments[i].renderBuffer());
+ rpD->colorFormat[i] = texD ? texD->d->format : rbD->d->format;
+ }
+
+ if (m_desc.depthTexture())
+ rpD->dsFormat = QRHI_RES(QMetalTexture, m_desc.depthTexture())->d->format;
+ else if (m_desc.depthStencilBuffer())
+ rpD->dsFormat = QRHI_RES(QMetalRenderBuffer, m_desc.depthStencilBuffer())->d->format;
+
+ return rpD;
+}
+
+bool QMetalTextureRenderTarget::build()
+{
+ const QVector<QRhiColorAttachment> colorAttachments = m_desc.colorAttachments();
+ Q_ASSERT(!colorAttachments.isEmpty() || m_desc.depthTexture());
+ Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
+ const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
+
+ d->colorAttCount = colorAttachments.count();
+ for (int i = 0; i < d->colorAttCount; ++i) {
+ QMetalTexture *texD = QRHI_RES(QMetalTexture, colorAttachments[i].texture());
+ QMetalRenderBuffer *rbD = QRHI_RES(QMetalRenderBuffer, colorAttachments[i].renderBuffer());
+ Q_ASSERT(texD || rbD);
+ id<MTLTexture> dst = nil;
+ if (texD) {
+ dst = texD->d->tex;
+ if (i == 0) {
+ d->pixelSize = texD->pixelSize();
+ d->sampleCount = texD->samples;
+ }
+ } else if (rbD) {
+ dst = rbD->d->tex;
+ if (i == 0) {
+ d->pixelSize = rbD->pixelSize();
+ d->sampleCount = rbD->samples;
+ }
+ }
+ QMetalRenderTargetData::ColorAtt colorAtt;
+ colorAtt.tex = dst;
+ colorAtt.layer = colorAttachments[i].layer();
+ colorAtt.level = colorAttachments[i].level();
+ QMetalTexture *resTexD = QRHI_RES(QMetalTexture, colorAttachments[i].resolveTexture());
+ colorAtt.resolveTex = resTexD ? resTexD->d->tex : nil;
+ colorAtt.resolveLayer = colorAttachments[i].resolveLayer();
+ colorAtt.resolveLevel = colorAttachments[i].resolveLevel();
+ d->fb.colorAtt[i] = colorAtt;
+ }
+ d->dpr = 1;
+
+ if (hasDepthStencil) {
+ if (m_desc.depthTexture()) {
+ QMetalTexture *depthTexD = QRHI_RES(QMetalTexture, m_desc.depthTexture());
+ d->fb.dsTex = depthTexD->d->tex;
+ d->fb.hasStencil = false;
+ d->fb.depthNeedsStore = true;
+ if (d->colorAttCount == 0) {
+ d->pixelSize = depthTexD->pixelSize();
+ d->sampleCount = depthTexD->samples;
+ }
+ } else {
+ QMetalRenderBuffer *depthRbD = QRHI_RES(QMetalRenderBuffer, m_desc.depthStencilBuffer());
+ d->fb.dsTex = depthRbD->d->tex;
+ d->fb.hasStencil = true;
+ d->fb.depthNeedsStore = false;
+ if (d->colorAttCount == 0) {
+ d->pixelSize = depthRbD->pixelSize();
+ d->sampleCount = depthRbD->samples;
+ }
+ }
+ d->dsAttCount = 1;
+ } else {
+ d->dsAttCount = 0;
+ }
+
+ return true;
+}
+
+QSize QMetalTextureRenderTarget::pixelSize() const
+{
+ return d->pixelSize;
+}
+
+float QMetalTextureRenderTarget::devicePixelRatio() const
+{
+ return d->dpr;
+}
+
+int QMetalTextureRenderTarget::sampleCount() const
+{
+ return d->sampleCount;
+}
+
+QMetalShaderResourceBindings::QMetalShaderResourceBindings(QRhiImplementation *rhi)
+ : QRhiShaderResourceBindings(rhi)
+{
+}
+
+QMetalShaderResourceBindings::~QMetalShaderResourceBindings()
+{
+ release();
+}
+
+void QMetalShaderResourceBindings::release()
+{
+ sortedBindings.clear();
+ maxBinding = -1;
+}
+
+bool QMetalShaderResourceBindings::build()
+{
+ if (!sortedBindings.isEmpty())
+ release();
+
+ sortedBindings = m_bindings;
+ std::sort(sortedBindings.begin(), sortedBindings.end(),
+ [](const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b)
+ {
+ return QRhiShaderResourceBindingPrivate::get(&a)->binding < QRhiShaderResourceBindingPrivate::get(&b)->binding;
+ });
+ if (!sortedBindings.isEmpty())
+ maxBinding = QRhiShaderResourceBindingPrivate::get(&sortedBindings.last())->binding;
+ else
+ maxBinding = -1;
+
+ boundResourceData.resize(sortedBindings.count());
+
+ for (int i = 0, ie = sortedBindings.count(); i != ie; ++i) {
+ const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&sortedBindings[i]);
+ QMetalShaderResourceBindings::BoundResourceData &bd(boundResourceData[i]);
+ switch (b->type) {
+ case QRhiShaderResourceBinding::UniformBuffer:
+ {
+ QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.ubuf.buf);
+ bd.ubuf.id = bufD->m_id;
+ bd.ubuf.generation = bufD->generation;
+ }
+ break;
+ case QRhiShaderResourceBinding::SampledTexture:
+ {
+ QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.tex);
+ QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.sampler);
+ bd.stex.texId = texD->m_id;
+ bd.stex.texGeneration = texD->generation;
+ bd.stex.samplerId = samplerD->m_id;
+ bd.stex.samplerGeneration = samplerD->generation;
+ }
+ break;
+ case QRhiShaderResourceBinding::ImageLoad:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::ImageStore:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::ImageLoadStore:
+ {
+ QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.simage.tex);
+ bd.simage.id = texD->m_id;
+ bd.simage.generation = texD->generation;
+ }
+ break;
+ case QRhiShaderResourceBinding::BufferLoad:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::BufferStore:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::BufferLoadStore:
+ {
+ QMetalBuffer *bufD = QRHI_RES(QMetalBuffer, b->u.sbuf.buf);
+ bd.sbuf.id = bufD->m_id;
+ bd.sbuf.generation = bufD->generation;
+ }
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+
+ generation += 1;
+ return true;
+}
+
+QMetalGraphicsPipeline::QMetalGraphicsPipeline(QRhiImplementation *rhi)
+ : QRhiGraphicsPipeline(rhi),
+ d(new QMetalGraphicsPipelineData)
+{
+}
+
+QMetalGraphicsPipeline::~QMetalGraphicsPipeline()
+{
+ release();
+ delete d;
+}
+
+void QMetalGraphicsPipeline::release()
+{
+ QRHI_RES_RHI(QRhiMetal);
+
+ if (!d->ps)
+ return;
+
+ if (d->ps) {
+ [d->ps release];
+ d->ps = nil;
+ }
+
+ if (d->ds) {
+ [d->ds release];
+ d->ds = nil;
+ }
+
+ if (d->vsFunc) {
+ [d->vsFunc release];
+ d->vsFunc = nil;
+ }
+ if (d->vsLib) {
+ [d->vsLib release];
+ d->vsLib = nil;
+ }
+
+ if (d->fsFunc) {
+ [d->fsFunc release];
+ d->fsFunc = nil;
+ }
+ if (d->fsLib) {
+ [d->fsLib release];
+ d->fsLib = nil;
+ }
+
+ rhiD->unregisterResource(this);
+}
+
+static inline MTLVertexFormat toMetalAttributeFormat(QRhiVertexInputAttribute::Format format)
+{
+ switch (format) {
+ case QRhiVertexInputAttribute::Float4:
+ return MTLVertexFormatFloat4;
+ case QRhiVertexInputAttribute::Float3:
+ return MTLVertexFormatFloat3;
+ case QRhiVertexInputAttribute::Float2:
+ return MTLVertexFormatFloat2;
+ case QRhiVertexInputAttribute::Float:
+ return MTLVertexFormatFloat;
+ case QRhiVertexInputAttribute::UNormByte4:
+ return MTLVertexFormatUChar4Normalized;
+ case QRhiVertexInputAttribute::UNormByte2:
+ return MTLVertexFormatUChar2Normalized;
+ case QRhiVertexInputAttribute::UNormByte:
+ if (@available(macOS 10.13, iOS 11.0, *))
+ return MTLVertexFormatUCharNormalized;
+ else
+ Q_UNREACHABLE();
+ default:
+ Q_UNREACHABLE();
+ return MTLVertexFormatFloat4;
+ }
+}
+
+static inline MTLBlendFactor toMetalBlendFactor(QRhiGraphicsPipeline::BlendFactor f)
+{
+ switch (f) {
+ case QRhiGraphicsPipeline::Zero:
+ return MTLBlendFactorZero;
+ case QRhiGraphicsPipeline::One:
+ return MTLBlendFactorOne;
+ case QRhiGraphicsPipeline::SrcColor:
+ return MTLBlendFactorSourceColor;
+ case QRhiGraphicsPipeline::OneMinusSrcColor:
+ return MTLBlendFactorOneMinusSourceColor;
+ case QRhiGraphicsPipeline::DstColor:
+ return MTLBlendFactorDestinationColor;
+ case QRhiGraphicsPipeline::OneMinusDstColor:
+ return MTLBlendFactorOneMinusDestinationColor;
+ case QRhiGraphicsPipeline::SrcAlpha:
+ return MTLBlendFactorSourceAlpha;
+ case QRhiGraphicsPipeline::OneMinusSrcAlpha:
+ return MTLBlendFactorOneMinusSourceAlpha;
+ case QRhiGraphicsPipeline::DstAlpha:
+ return MTLBlendFactorDestinationAlpha;
+ case QRhiGraphicsPipeline::OneMinusDstAlpha:
+ return MTLBlendFactorOneMinusDestinationAlpha;
+ case QRhiGraphicsPipeline::ConstantColor:
+ return MTLBlendFactorBlendColor;
+ case QRhiGraphicsPipeline::ConstantAlpha:
+ return MTLBlendFactorBlendAlpha;
+ case QRhiGraphicsPipeline::OneMinusConstantColor:
+ return MTLBlendFactorOneMinusBlendColor;
+ case QRhiGraphicsPipeline::OneMinusConstantAlpha:
+ return MTLBlendFactorOneMinusBlendAlpha;
+ case QRhiGraphicsPipeline::SrcAlphaSaturate:
+ return MTLBlendFactorSourceAlphaSaturated;
+ case QRhiGraphicsPipeline::Src1Color:
+ return MTLBlendFactorSource1Color;
+ case QRhiGraphicsPipeline::OneMinusSrc1Color:
+ return MTLBlendFactorOneMinusSource1Color;
+ case QRhiGraphicsPipeline::Src1Alpha:
+ return MTLBlendFactorSource1Alpha;
+ case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
+ return MTLBlendFactorOneMinusSource1Alpha;
+ default:
+ Q_UNREACHABLE();
+ return MTLBlendFactorZero;
+ }
+}
+
+static inline MTLBlendOperation toMetalBlendOp(QRhiGraphicsPipeline::BlendOp op)
+{
+ switch (op) {
+ case QRhiGraphicsPipeline::Add:
+ return MTLBlendOperationAdd;
+ case QRhiGraphicsPipeline::Subtract:
+ return MTLBlendOperationSubtract;
+ case QRhiGraphicsPipeline::ReverseSubtract:
+ return MTLBlendOperationReverseSubtract;
+ case QRhiGraphicsPipeline::Min:
+ return MTLBlendOperationMin;
+ case QRhiGraphicsPipeline::Max:
+ return MTLBlendOperationMax;
+ default:
+ Q_UNREACHABLE();
+ return MTLBlendOperationAdd;
+ }
+}
+
+static inline uint toMetalColorWriteMask(QRhiGraphicsPipeline::ColorMask c)
+{
+ uint f = 0;
+ if (c.testFlag(QRhiGraphicsPipeline::R))
+ f |= MTLColorWriteMaskRed;
+ if (c.testFlag(QRhiGraphicsPipeline::G))
+ f |= MTLColorWriteMaskGreen;
+ if (c.testFlag(QRhiGraphicsPipeline::B))
+ f |= MTLColorWriteMaskBlue;
+ if (c.testFlag(QRhiGraphicsPipeline::A))
+ f |= MTLColorWriteMaskAlpha;
+ return f;
+}
+
+static inline MTLCompareFunction toMetalCompareOp(QRhiGraphicsPipeline::CompareOp op)
+{
+ switch (op) {
+ case QRhiGraphicsPipeline::Never:
+ return MTLCompareFunctionNever;
+ case QRhiGraphicsPipeline::Less:
+ return MTLCompareFunctionLess;
+ case QRhiGraphicsPipeline::Equal:
+ return MTLCompareFunctionEqual;
+ case QRhiGraphicsPipeline::LessOrEqual:
+ return MTLCompareFunctionLessEqual;
+ case QRhiGraphicsPipeline::Greater:
+ return MTLCompareFunctionGreater;
+ case QRhiGraphicsPipeline::NotEqual:
+ return MTLCompareFunctionNotEqual;
+ case QRhiGraphicsPipeline::GreaterOrEqual:
+ return MTLCompareFunctionGreaterEqual;
+ case QRhiGraphicsPipeline::Always:
+ return MTLCompareFunctionAlways;
+ default:
+ Q_UNREACHABLE();
+ return MTLCompareFunctionAlways;
+ }
+}
+
+static inline MTLStencilOperation toMetalStencilOp(QRhiGraphicsPipeline::StencilOp op)
+{
+ switch (op) {
+ case QRhiGraphicsPipeline::StencilZero:
+ return MTLStencilOperationZero;
+ case QRhiGraphicsPipeline::Keep:
+ return MTLStencilOperationKeep;
+ case QRhiGraphicsPipeline::Replace:
+ return MTLStencilOperationReplace;
+ case QRhiGraphicsPipeline::IncrementAndClamp:
+ return MTLStencilOperationIncrementClamp;
+ case QRhiGraphicsPipeline::DecrementAndClamp:
+ return MTLStencilOperationDecrementClamp;
+ case QRhiGraphicsPipeline::Invert:
+ return MTLStencilOperationInvert;
+ case QRhiGraphicsPipeline::IncrementAndWrap:
+ return MTLStencilOperationIncrementWrap;
+ case QRhiGraphicsPipeline::DecrementAndWrap:
+ return MTLStencilOperationDecrementWrap;
+ default:
+ Q_UNREACHABLE();
+ return MTLStencilOperationKeep;
+ }
+}
+
+static inline MTLPrimitiveType toMetalPrimitiveType(QRhiGraphicsPipeline::Topology t)
+{
+ switch (t) {
+ case QRhiGraphicsPipeline::Triangles:
+ return MTLPrimitiveTypeTriangle;
+ case QRhiGraphicsPipeline::TriangleStrip:
+ return MTLPrimitiveTypeTriangleStrip;
+ case QRhiGraphicsPipeline::Lines:
+ return MTLPrimitiveTypeLine;
+ case QRhiGraphicsPipeline::LineStrip:
+ return MTLPrimitiveTypeLineStrip;
+ case QRhiGraphicsPipeline::Points:
+ return MTLPrimitiveTypePoint;
+ default:
+ Q_UNREACHABLE();
+ return MTLPrimitiveTypeTriangle;
+ }
+}
+
+static inline MTLCullMode toMetalCullMode(QRhiGraphicsPipeline::CullMode c)
+{
+ switch (c) {
+ case QRhiGraphicsPipeline::None:
+ return MTLCullModeNone;
+ case QRhiGraphicsPipeline::Front:
+ return MTLCullModeFront;
+ case QRhiGraphicsPipeline::Back:
+ return MTLCullModeBack;
+ default:
+ Q_UNREACHABLE();
+ return MTLCullModeNone;
+ }
+}
+
+id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Variant shaderVariant,
+ QString *error, QByteArray *entryPoint)
+{
+ QShaderCode mtllib = shader.shader({ QShader::MetalLibShader, 12, shaderVariant });
+ if (!mtllib.shader().isEmpty()) {
+ dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(),
+ mtllib.shader().size(),
+ dispatch_get_global_queue(0, 0),
+ DISPATCH_DATA_DESTRUCTOR_DEFAULT);
+ NSError *err = nil;
+ id<MTLLibrary> lib = [dev newLibraryWithData: data error: &err];
+ dispatch_release(data);
+ if (!err) {
+ *entryPoint = mtllib.entryPoint();
+ return lib;
+ } else {
+ const QString msg = QString::fromNSString(err.localizedDescription);
+ qWarning("Failed to load metallib from baked shader: %s", qPrintable(msg));
+ }
+ }
+
+ QShaderCode mslSource = shader.shader({ QShader::MslShader, 12, shaderVariant });
+ if (mslSource.shader().isEmpty()) {
+ qWarning() << "No MSL 1.2 code found in baked shader" << shader;
+ return nil;
+ }
+
+ NSString *src = [NSString stringWithUTF8String: mslSource.shader().constData()];
+ MTLCompileOptions *opts = [[MTLCompileOptions alloc] init];
+ opts.languageVersion = MTLLanguageVersion1_2;
+ NSError *err = nil;
+ id<MTLLibrary> lib = [dev newLibraryWithSource: src options: opts error: &err];
+ [opts release];
+ // src is autoreleased
+
+ if (err) {
+ const QString msg = QString::fromNSString(err.localizedDescription);
+ *error = msg;
+ return nil;
+ }
+
+ *entryPoint = mslSource.entryPoint();
+ return lib;
+}
+
+id<MTLFunction> QRhiMetalData::createMSLShaderFunction(id<MTLLibrary> lib, const QByteArray &entryPoint)
+{
+ NSString *name = [NSString stringWithUTF8String: entryPoint.constData()];
+ id<MTLFunction> f = [lib newFunctionWithName: name];
+ [name release];
+ return f;
+}
+
+bool QMetalGraphicsPipeline::build()
+{
+ if (d->ps)
+ release();
+
+ QRHI_RES_RHI(QRhiMetal);
+
+ // same binding space for vertex and constant buffers - work it around
+ const int firstVertexBinding = QRHI_RES(QMetalShaderResourceBindings, m_shaderResourceBindings)->maxBinding + 1;
+
+ MTLVertexDescriptor *inputLayout = [MTLVertexDescriptor vertexDescriptor];
+ const QVector<QRhiVertexInputAttribute> attributes = m_vertexInputLayout.attributes();
+ for (const QRhiVertexInputAttribute &attribute : attributes) {
+ const int loc = attribute.location();
+ inputLayout.attributes[loc].format = toMetalAttributeFormat(attribute.format());
+ inputLayout.attributes[loc].offset = attribute.offset();
+ inputLayout.attributes[loc].bufferIndex = firstVertexBinding + attribute.binding();
+ }
+ const QVector<QRhiVertexInputBinding> bindings = m_vertexInputLayout.bindings();
+ for (int i = 0, ie = bindings.count(); i != ie; ++i) {
+ const QRhiVertexInputBinding &binding(bindings[i]);
+ const int layoutIdx = firstVertexBinding + i;
+ inputLayout.layouts[layoutIdx].stepFunction =
+ binding.classification() == QRhiVertexInputBinding::PerInstance
+ ? MTLVertexStepFunctionPerInstance : MTLVertexStepFunctionPerVertex;
+ inputLayout.layouts[layoutIdx].stepRate = binding.instanceStepRate();
+ inputLayout.layouts[layoutIdx].stride = binding.stride();
+ }
+
+ MTLRenderPipelineDescriptor *rpDesc = [[MTLRenderPipelineDescriptor alloc] init];
+
+ rpDesc.vertexDescriptor = inputLayout;
+
+ // mutability cannot be determined (slotted buffers could be set as
+ // MTLMutabilityImmutable, but then we potentially need a different
+ // descriptor for each buffer combination as this depends on the actual
+ // buffers not just the resource binding layout) so leave it at the default
+
+ for (const QRhiShaderStage &shaderStage : qAsConst(m_shaderStages)) {
+ QString error;
+ QByteArray entryPoint;
+ id<MTLLibrary> lib = rhiD->d->createMetalLib(shaderStage.shader(), shaderStage.shaderVariant(), &error, &entryPoint);
+ if (!lib) {
+ qWarning("MSL shader compilation failed: %s", qPrintable(error));
+ return false;
+ }
+ id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
+ if (!func) {
+ qWarning("MSL function for entry point %s not found", entryPoint.constData());
+ [lib release];
+ return false;
+ }
+ switch (shaderStage.type()) {
+ case QRhiShaderStage::Vertex:
+ rpDesc.vertexFunction = func;
+ d->vsLib = lib;
+ d->vsFunc = func;
+ break;
+ case QRhiShaderStage::Fragment:
+ rpDesc.fragmentFunction = func;
+ d->fsLib = lib;
+ d->fsFunc = func;
+ break;
+ default:
+ [func release];
+ [lib release];
+ break;
+ }
+ }
+
+ QMetalRenderPassDescriptor *rpD = QRHI_RES(QMetalRenderPassDescriptor, m_renderPassDesc);
+
+ if (rpD->colorAttachmentCount) {
+ // defaults when no targetBlends are provided
+ rpDesc.colorAttachments[0].pixelFormat = MTLPixelFormat(rpD->colorFormat[0]);
+ rpDesc.colorAttachments[0].writeMask = MTLColorWriteMaskAll;
+ rpDesc.colorAttachments[0].blendingEnabled = false;
+
+ Q_ASSERT(m_targetBlends.count() == rpD->colorAttachmentCount
+ || (m_targetBlends.isEmpty() && rpD->colorAttachmentCount == 1));
+
+ for (int i = 0, ie = m_targetBlends.count(); i != ie; ++i) {
+ const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[i]);
+ rpDesc.colorAttachments[i].pixelFormat = MTLPixelFormat(rpD->colorFormat[i]);
+ rpDesc.colorAttachments[i].blendingEnabled = b.enable;
+ rpDesc.colorAttachments[i].sourceRGBBlendFactor = toMetalBlendFactor(b.srcColor);
+ rpDesc.colorAttachments[i].destinationRGBBlendFactor = toMetalBlendFactor(b.dstColor);
+ rpDesc.colorAttachments[i].rgbBlendOperation = toMetalBlendOp(b.opColor);
+ rpDesc.colorAttachments[i].sourceAlphaBlendFactor = toMetalBlendFactor(b.srcAlpha);
+ rpDesc.colorAttachments[i].destinationAlphaBlendFactor = toMetalBlendFactor(b.dstAlpha);
+ rpDesc.colorAttachments[i].alphaBlendOperation = toMetalBlendOp(b.opAlpha);
+ rpDesc.colorAttachments[i].writeMask = toMetalColorWriteMask(b.colorWrite);
+ }
+ }
+
+ if (rpD->hasDepthStencil) {
+ // Must only be set when a depth-stencil buffer will actually be bound,
+ // validation blows up otherwise.
+ MTLPixelFormat fmt = MTLPixelFormat(rpD->dsFormat);
+ rpDesc.depthAttachmentPixelFormat = fmt;
+ if (fmt != MTLPixelFormatDepth16Unorm && fmt != MTLPixelFormatDepth32Float)
+ rpDesc.stencilAttachmentPixelFormat = fmt;
+ }
+
+ rpDesc.sampleCount = rhiD->effectiveSampleCount(m_sampleCount);
+
+ NSError *err = nil;
+ d->ps = [rhiD->d->dev newRenderPipelineStateWithDescriptor: rpDesc error: &err];
+ if (!d->ps) {
+ const QString msg = QString::fromNSString(err.localizedDescription);
+ qWarning("Failed to create render pipeline state: %s", qPrintable(msg));
+ [rpDesc release];
+ return false;
+ }
+ [rpDesc release];
+
+ MTLDepthStencilDescriptor *dsDesc = [[MTLDepthStencilDescriptor alloc] init];
+ dsDesc.depthCompareFunction = m_depthTest ? toMetalCompareOp(m_depthOp) : MTLCompareFunctionAlways;
+ dsDesc.depthWriteEnabled = m_depthWrite;
+ if (m_stencilTest) {
+ dsDesc.frontFaceStencil = [[MTLStencilDescriptor alloc] init];
+ dsDesc.frontFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilFront.failOp);
+ dsDesc.frontFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilFront.depthFailOp);
+ dsDesc.frontFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilFront.passOp);
+ dsDesc.frontFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilFront.compareOp);
+ dsDesc.frontFaceStencil.readMask = m_stencilReadMask;
+ dsDesc.frontFaceStencil.writeMask = m_stencilWriteMask;
+
+ dsDesc.backFaceStencil = [[MTLStencilDescriptor alloc] init];
+ dsDesc.backFaceStencil.stencilFailureOperation = toMetalStencilOp(m_stencilBack.failOp);
+ dsDesc.backFaceStencil.depthFailureOperation = toMetalStencilOp(m_stencilBack.depthFailOp);
+ dsDesc.backFaceStencil.depthStencilPassOperation = toMetalStencilOp(m_stencilBack.passOp);
+ dsDesc.backFaceStencil.stencilCompareFunction = toMetalCompareOp(m_stencilBack.compareOp);
+ dsDesc.backFaceStencil.readMask = m_stencilReadMask;
+ dsDesc.backFaceStencil.writeMask = m_stencilWriteMask;
+ }
+
+ d->ds = [rhiD->d->dev newDepthStencilStateWithDescriptor: dsDesc];
+ [dsDesc release];
+
+ d->primitiveType = toMetalPrimitiveType(m_topology);
+ d->winding = m_frontFace == CCW ? MTLWindingCounterClockwise : MTLWindingClockwise;
+ d->cullMode = toMetalCullMode(m_cullMode);
+
+ lastActiveFrameSlot = -1;
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+QMetalComputePipeline::QMetalComputePipeline(QRhiImplementation *rhi)
+ : QRhiComputePipeline(rhi),
+ d(new QMetalComputePipelineData)
+{
+}
+
+QMetalComputePipeline::~QMetalComputePipeline()
+{
+ release();
+ delete d;
+}
+
+void QMetalComputePipeline::release()
+{
+ QRHI_RES_RHI(QRhiMetal);
+
+ if (d->csFunc) {
+ [d->csFunc release];
+ d->csFunc = nil;
+ }
+ if (d->csLib) {
+ [d->csLib release];
+ d->csLib = nil;
+ }
+
+ if (!d->ps)
+ return;
+
+ if (d->ps) {
+ [d->ps release];
+ d->ps = nil;
+ }
+
+ rhiD->unregisterResource(this);
+}
+
+bool QMetalComputePipeline::build()
+{
+ if (d->ps)
+ release();
+
+ QRHI_RES_RHI(QRhiMetal);
+
+ const QShader shader = m_shaderStage.shader();
+ QString error;
+ QByteArray entryPoint;
+ id<MTLLibrary> lib = rhiD->d->createMetalLib(shader, m_shaderStage.shaderVariant(),
+ &error, &entryPoint);
+ if (!lib) {
+ qWarning("MSL shader compilation failed: %s", qPrintable(error));
+ return false;
+ }
+ id<MTLFunction> func = rhiD->d->createMSLShaderFunction(lib, entryPoint);
+ if (!func) {
+ qWarning("MSL function for entry point %s not found", entryPoint.constData());
+ [lib release];
+ return false;
+ }
+ d->csLib = lib;
+ d->csFunc = func;
+ std::array<uint, 3> localSize = shader.description().computeShaderLocalSize();
+ d->localSize = MTLSizeMake(localSize[0], localSize[1], localSize[2]);
+
+ NSError *err = nil;
+ d->ps = [rhiD->d->dev newComputePipelineStateWithFunction: d->csFunc error: &err];
+ if (!d->ps) {
+ const QString msg = QString::fromNSString(err.localizedDescription);
+ qWarning("Failed to create render pipeline state: %s", qPrintable(msg));
+ return false;
+ }
+
+ lastActiveFrameSlot = -1;
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+QMetalCommandBuffer::QMetalCommandBuffer(QRhiImplementation *rhi)
+ : QRhiCommandBuffer(rhi),
+ d(new QMetalCommandBufferData)
+{
+ resetState();
+}
+
+QMetalCommandBuffer::~QMetalCommandBuffer()
+{
+ release();
+ delete d;
+}
+
+void QMetalCommandBuffer::release()
+{
+ // nothing to do here, we do not own the MTL cb object
+}
+
+const QRhiNativeHandles *QMetalCommandBuffer::nativeHandles()
+{
+ nativeHandlesStruct.commandBuffer = d->cb;
+ nativeHandlesStruct.encoder = d->currentRenderPassEncoder;
+ return &nativeHandlesStruct;
+}
+
+void QMetalCommandBuffer::resetState()
+{
+ d->currentRenderPassEncoder = nil;
+ d->currentComputePassEncoder = nil;
+ d->currentPassRpDesc = nil;
+ resetPerPassState();
+}
+
+void QMetalCommandBuffer::resetPerPassState()
+{
+ recordingPass = NoPass;
+ currentTarget = nullptr;
+ resetPerPassCachedState();
+}
+
+void QMetalCommandBuffer::resetPerPassCachedState()
+{
+ currentGraphicsPipeline = nullptr;
+ currentComputePipeline = nullptr;
+ currentPipelineGeneration = 0;
+ currentGraphicsSrb = nullptr;
+ currentComputeSrb = nullptr;
+ currentSrbGeneration = 0;
+ currentResSlot = -1;
+ currentIndexBuffer = nullptr;
+
+ d->currentFirstVertexBinding = -1;
+ d->currentVertexInputsBuffers.clear();
+ d->currentVertexInputOffsets.clear();
+}
+
+QMetalSwapChain::QMetalSwapChain(QRhiImplementation *rhi)
+ : QRhiSwapChain(rhi),
+ rtWrapper(rhi),
+ cbWrapper(rhi),
+ d(new QMetalSwapChainData)
+{
+ for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
+ d->sem[i] = nullptr;
+ d->msaaTex[i] = nil;
+ }
+}
+
+QMetalSwapChain::~QMetalSwapChain()
+{
+ release();
+ delete d;
+}
+
+void QMetalSwapChain::release()
+{
+ if (!d->layer)
+ return;
+
+ for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
+ if (d->sem[i]) {
+ // the semaphores cannot be released if they do not have the initial value
+ dispatch_semaphore_wait(d->sem[i], DISPATCH_TIME_FOREVER);
+ dispatch_semaphore_signal(d->sem[i]);
+
+ dispatch_release(d->sem[i]);
+ d->sem[i] = nullptr;
+ }
+ }
+
+ for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
+ [d->msaaTex[i] release];
+ d->msaaTex[i] = nil;
+ }
+
+ d->layer = nullptr;
+
+ QRHI_RES_RHI(QRhiMetal);
+ rhiD->swapchains.remove(this);
+
+ QRHI_PROF;
+ QRHI_PROF_F(releaseSwapChain(this));
+
+ rhiD->unregisterResource(this);
+}
+
+QRhiCommandBuffer *QMetalSwapChain::currentFrameCommandBuffer()
+{
+ return &cbWrapper;
+}
+
+QRhiRenderTarget *QMetalSwapChain::currentFrameRenderTarget()
+{
+ return &rtWrapper;
+}
+
+QSize QMetalSwapChain::surfacePixelSize()
+{
+ // may be called before build, must not access other than m_*
+
+ NSView *v = (NSView *) m_window->winId();
+ if (v) {
+ CAMetalLayer *layer = (CAMetalLayer *) [v layer];
+ if (layer) {
+ CGSize size = [layer drawableSize];
+ return QSize(size.width, size.height);
+ }
+ }
+ return QSize();
+}
+
+QRhiRenderPassDescriptor *QMetalSwapChain::newCompatibleRenderPassDescriptor()
+{
+ chooseFormats(); // ensure colorFormat and similar are filled out
+
+ QRHI_RES_RHI(QRhiMetal);
+ QMetalRenderPassDescriptor *rpD = new QMetalRenderPassDescriptor(m_rhi);
+ rpD->colorAttachmentCount = 1;
+ rpD->hasDepthStencil = m_depthStencil != nullptr;
+
+ rpD->colorFormat[0] = d->colorFormat;
+
+ // m_depthStencil may not be built yet so cannot rely on computed fields in it
+ rpD->dsFormat = rhiD->d->dev.depth24Stencil8PixelFormatSupported
+ ? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
+
+ return rpD;
+}
+
+void QMetalSwapChain::chooseFormats()
+{
+ QRHI_RES_RHI(QRhiMetal);
+ samples = rhiD->effectiveSampleCount(m_sampleCount);
+ // pick a format that is allowed for CAMetalLayer.pixelFormat
+ d->colorFormat = m_flags.testFlag(sRGB) ? MTLPixelFormatBGRA8Unorm_sRGB : MTLPixelFormatBGRA8Unorm;
+ d->rhiColorFormat = QRhiTexture::BGRA8;
+}
+
+bool QMetalSwapChain::buildOrResize()
+{
+ Q_ASSERT(m_window);
+
+ const bool needsRegistration = !window || window != m_window;
+
+ if (window && window != m_window)
+ release();
+ // else no release(), this is intentional
+
+ QRHI_RES_RHI(QRhiMetal);
+ if (needsRegistration)
+ rhiD->swapchains.insert(this);
+
+ window = m_window;
+
+ if (window->surfaceType() != QSurface::MetalSurface) {
+ qWarning("QMetalSwapChain only supports MetalSurface windows");
+ return false;
+ }
+
+ NSView *v = (NSView *) window->winId();
+ d->layer = (CAMetalLayer *) [v layer];
+ Q_ASSERT(d->layer);
+
+ chooseFormats();
+ if (d->colorFormat != d->layer.pixelFormat)
+ d->layer.pixelFormat = d->colorFormat;
+
+ if (m_flags.testFlag(UsedAsTransferSource))
+ d->layer.framebufferOnly = NO;
+
+#ifdef Q_OS_MACOS
+ if (m_flags.testFlag(NoVSync)) {
+ if (@available(macOS 10.13, *))
+ d->layer.displaySyncEnabled = NO;
+ }
+#endif
+
+ m_currentPixelSize = surfacePixelSize();
+ pixelSize = m_currentPixelSize;
+
+ [d->layer setDevice: rhiD->d->dev];
+
+ d->curDrawable = nil;
+
+ for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
+ if (!d->sem[i])
+ d->sem[i] = dispatch_semaphore_create(QMTL_FRAMES_IN_FLIGHT - 1);
+ }
+
+ currentFrameSlot = 0;
+ frameCount = 0;
+
+ ds = m_depthStencil ? QRHI_RES(QMetalRenderBuffer, m_depthStencil) : nullptr;
+ if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) {
+ qWarning("Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
+ m_depthStencil->sampleCount(), m_sampleCount);
+ }
+ if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) {
+ qWarning("Depth-stencil buffer's size (%dx%d) does not match the layer size (%dx%d). Expect problems.",
+ m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(),
+ pixelSize.width(), pixelSize.height());
+ }
+
+ rtWrapper.d->pixelSize = pixelSize;
+ rtWrapper.d->dpr = window->devicePixelRatio();
+ rtWrapper.d->sampleCount = samples;
+ rtWrapper.d->colorAttCount = 1;
+ rtWrapper.d->dsAttCount = ds ? 1 : 0;
+
+ qDebug("got CAMetalLayer, size %dx%d", pixelSize.width(), pixelSize.height());
+
+ if (samples > 1) {
+ MTLTextureDescriptor *desc = [[MTLTextureDescriptor alloc] init];
+ desc.textureType = MTLTextureType2DMultisample;
+ desc.pixelFormat = d->colorFormat;
+ desc.width = pixelSize.width();
+ desc.height = pixelSize.height();
+ desc.sampleCount = samples;
+ desc.resourceOptions = MTLResourceStorageModePrivate;
+ desc.storageMode = MTLStorageModePrivate;
+ desc.usage = MTLTextureUsageRenderTarget;
+ for (int i = 0; i < QMTL_FRAMES_IN_FLIGHT; ++i) {
+ [d->msaaTex[i] release];
+ d->msaaTex[i] = [rhiD->d->dev newTextureWithDescriptor: desc];
+ }
+ [desc release];
+ }
+
+ QRHI_PROF;
+ QRHI_PROF_F(resizeSwapChain(this, QMTL_FRAMES_IN_FLIGHT, samples > 1 ? QMTL_FRAMES_IN_FLIGHT : 0, samples));
+
+ if (needsRegistration)
+ rhiD->registerResource(this);
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/rhi/qrhimetal_p.h b/src/gui/rhi/qrhimetal_p.h
new file mode 100644
index 0000000000..094801c58c
--- /dev/null
+++ b/src/gui/rhi/qrhimetal_p.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHIMETAL_H
+#define QRHIMETAL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qrhi_p.h>
+
+// no Metal includes here, the user code may be plain C++
+
+QT_BEGIN_NAMESPACE
+
+struct Q_GUI_EXPORT QRhiMetalInitParams : public QRhiInitParams
+{
+};
+
+struct Q_GUI_EXPORT QRhiMetalNativeHandles : public QRhiNativeHandles
+{
+ void *dev = nullptr; // id<MTLDevice>
+ void *cmdQueue = nullptr; // id<MTLCommandQueue>
+};
+
+struct Q_GUI_EXPORT QRhiMetalTextureNativeHandles : public QRhiNativeHandles
+{
+ void *texture = nullptr; // id<MTLTexture>
+};
+
+struct Q_GUI_EXPORT QRhiMetalCommandBufferNativeHandles : public QRhiNativeHandles
+{
+ void *commandBuffer = nullptr; // id<MTLCommandBuffer>
+ void *encoder = nullptr; // id<MTLRenderCommandEncoder>
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhimetal_p_p.h b/src/gui/rhi/qrhimetal_p_p.h
new file mode 100644
index 0000000000..8b0256991d
--- /dev/null
+++ b/src/gui/rhi/qrhimetal_p_p.h
@@ -0,0 +1,450 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHIMETAL_P_H
+#define QRHIMETAL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qrhimetal_p.h"
+#include "qrhi_p_p.h"
+#include <QWindow>
+
+QT_BEGIN_NAMESPACE
+
+static const int QMTL_FRAMES_IN_FLIGHT = 2;
+
+// have to hide the ObjC stuff, this header cannot contain MTL* at all
+struct QMetalBufferData;
+
+struct QMetalBuffer : public QRhiBuffer
+{
+ QMetalBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size);
+ ~QMetalBuffer();
+ void release() override;
+ bool build() override;
+
+ QMetalBufferData *d;
+ uint generation = 0;
+ int lastActiveFrameSlot = -1;
+ friend class QRhiMetal;
+ friend struct QMetalShaderResourceBindings;
+};
+
+struct QMetalRenderBufferData;
+
+struct QMetalRenderBuffer : public QRhiRenderBuffer
+{
+ QMetalRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags);
+ ~QMetalRenderBuffer();
+ void release() override;
+ bool build() override;
+ QRhiTexture::Format backingFormat() const override;
+
+ QMetalRenderBufferData *d;
+ int samples = 1;
+ uint generation = 0;
+ int lastActiveFrameSlot = -1;
+ friend class QRhiMetal;
+};
+
+struct QMetalTextureData;
+
+struct QMetalTexture : public QRhiTexture
+{
+ QMetalTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+ int sampleCount, Flags flags);
+ ~QMetalTexture();
+ void release() override;
+ bool build() override;
+ bool buildFrom(const QRhiNativeHandles *src) override;
+ const QRhiNativeHandles *nativeHandles() override;
+
+ bool prepareBuild(QSize *adjustedSize = nullptr);
+
+ QMetalTextureData *d;
+ QRhiMetalTextureNativeHandles nativeHandlesStruct;
+ int mipLevelCount = 0;
+ int samples = 1;
+ uint generation = 0;
+ int lastActiveFrameSlot = -1;
+ friend class QRhiMetal;
+ friend struct QMetalShaderResourceBindings;
+ friend struct QMetalTextureData;
+};
+
+struct QMetalSamplerData;
+
+struct QMetalSampler : public QRhiSampler
+{
+ QMetalSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
+ AddressMode u, AddressMode v);
+ ~QMetalSampler();
+ void release() override;
+ bool build() override;
+
+ QMetalSamplerData *d;
+ uint generation = 0;
+ int lastActiveFrameSlot = -1;
+ friend class QRhiMetal;
+ friend struct QMetalShaderResourceBindings;
+};
+
+struct QMetalRenderPassDescriptor : public QRhiRenderPassDescriptor
+{
+ QMetalRenderPassDescriptor(QRhiImplementation *rhi);
+ ~QMetalRenderPassDescriptor();
+ void release() override;
+
+ // there is no MTLRenderPassDescriptor here as one will be created for each pass in beginPass()
+
+ // but the things needed for the render pipeline descriptor have to be provided
+ static const int MAX_COLOR_ATTACHMENTS = 8;
+ int colorAttachmentCount = 0;
+ bool hasDepthStencil = false;
+ int colorFormat[MAX_COLOR_ATTACHMENTS];
+ int dsFormat;
+};
+
+struct QMetalRenderTargetData;
+
+struct QMetalReferenceRenderTarget : public QRhiRenderTarget
+{
+ QMetalReferenceRenderTarget(QRhiImplementation *rhi);
+ ~QMetalReferenceRenderTarget();
+ void release() override;
+
+ QSize pixelSize() const override;
+ float devicePixelRatio() const override;
+ int sampleCount() const override;
+
+ QMetalRenderTargetData *d;
+};
+
+struct QMetalTextureRenderTarget : public QRhiTextureRenderTarget
+{
+ QMetalTextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc, Flags flags);
+ ~QMetalTextureRenderTarget();
+ void release() override;
+
+ QSize pixelSize() const override;
+ float devicePixelRatio() const override;
+ int sampleCount() const override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+ bool build() override;
+
+ QMetalRenderTargetData *d;
+ friend class QRhiMetal;
+};
+
+struct QMetalShaderResourceBindings : public QRhiShaderResourceBindings
+{
+ QMetalShaderResourceBindings(QRhiImplementation *rhi);
+ ~QMetalShaderResourceBindings();
+ void release() override;
+ bool build() override;
+
+ QVector<QRhiShaderResourceBinding> sortedBindings;
+ int maxBinding = -1;
+
+ struct BoundUniformBufferData {
+ quint64 id;
+ uint generation;
+ };
+ struct BoundSampledTextureData {
+ quint64 texId;
+ uint texGeneration;
+ quint64 samplerId;
+ uint samplerGeneration;
+ };
+ struct BoundStorageImageData {
+ quint64 id;
+ uint generation;
+ };
+ struct BoundStorageBufferData {
+ quint64 id;
+ uint generation;
+ };
+ struct BoundResourceData {
+ union {
+ BoundUniformBufferData ubuf;
+ BoundSampledTextureData stex;
+ BoundStorageImageData simage;
+ BoundStorageBufferData sbuf;
+ };
+ };
+ QVector<BoundResourceData> boundResourceData;
+
+ uint generation = 0;
+ friend class QRhiMetal;
+};
+
+struct QMetalGraphicsPipelineData;
+
+struct QMetalGraphicsPipeline : public QRhiGraphicsPipeline
+{
+ QMetalGraphicsPipeline(QRhiImplementation *rhi);
+ ~QMetalGraphicsPipeline();
+ void release() override;
+ bool build() override;
+
+ QMetalGraphicsPipelineData *d;
+ uint generation = 0;
+ int lastActiveFrameSlot = -1;
+ friend class QRhiMetal;
+};
+
+struct QMetalComputePipelineData;
+
+struct QMetalComputePipeline : public QRhiComputePipeline
+{
+ QMetalComputePipeline(QRhiImplementation *rhi);
+ ~QMetalComputePipeline();
+ void release() override;
+ bool build() override;
+
+ QMetalComputePipelineData *d;
+ uint generation = 0;
+ int lastActiveFrameSlot = -1;
+ friend class QRhiMetal;
+};
+
+struct QMetalCommandBufferData;
+struct QMetalSwapChain;
+
+struct QMetalCommandBuffer : public QRhiCommandBuffer
+{
+ QMetalCommandBuffer(QRhiImplementation *rhi);
+ ~QMetalCommandBuffer();
+ void release() override;
+
+ QMetalCommandBufferData *d = nullptr;
+ QRhiMetalCommandBufferNativeHandles nativeHandlesStruct;
+
+ enum PassType {
+ NoPass,
+ RenderPass,
+ ComputePass
+ };
+
+ PassType recordingPass;
+ QRhiRenderTarget *currentTarget;
+ QRhiGraphicsPipeline *currentGraphicsPipeline;
+ QRhiComputePipeline *currentComputePipeline;
+ uint currentPipelineGeneration;
+ QRhiShaderResourceBindings *currentGraphicsSrb;
+ QRhiShaderResourceBindings *currentComputeSrb;
+ uint currentSrbGeneration;
+ int currentResSlot;
+ QRhiBuffer *currentIndexBuffer;
+ quint32 currentIndexOffset;
+ QRhiCommandBuffer::IndexFormat currentIndexFormat;
+
+ const QRhiNativeHandles *nativeHandles();
+ void resetState();
+ void resetPerPassState();
+ void resetPerPassCachedState();
+};
+
+struct QMetalSwapChainData;
+
+struct QMetalSwapChain : public QRhiSwapChain
+{
+ QMetalSwapChain(QRhiImplementation *rhi);
+ ~QMetalSwapChain();
+ void release() override;
+
+ QRhiCommandBuffer *currentFrameCommandBuffer() override;
+ QRhiRenderTarget *currentFrameRenderTarget() override;
+ QSize surfacePixelSize() override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+
+ bool buildOrResize() override;
+
+ void chooseFormats();
+
+ QWindow *window = nullptr;
+ QSize pixelSize;
+ int currentFrameSlot = 0; // 0..QMTL_FRAMES_IN_FLIGHT-1
+ int frameCount = 0;
+ int samples = 1;
+ QMetalReferenceRenderTarget rtWrapper;
+ QMetalCommandBuffer cbWrapper;
+ QMetalRenderBuffer *ds = nullptr;
+ QMetalSwapChainData *d = nullptr;
+};
+
+struct QRhiMetalData;
+
+class QRhiMetal : public QRhiImplementation
+{
+public:
+ QRhiMetal(QRhiMetalInitParams *params, QRhiMetalNativeHandles *importDevice = nullptr);
+ ~QRhiMetal();
+
+ bool create(QRhi::Flags flags) override;
+ void destroy() override;
+
+ QRhiGraphicsPipeline *createGraphicsPipeline() override;
+ QRhiComputePipeline *createComputePipeline() override;
+ QRhiShaderResourceBindings *createShaderResourceBindings() override;
+ QRhiBuffer *createBuffer(QRhiBuffer::Type type,
+ QRhiBuffer::UsageFlags usage,
+ int size) override;
+ QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiRenderBuffer::Flags flags) override;
+ QRhiTexture *createTexture(QRhiTexture::Format format,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiTexture::Flags flags) override;
+ QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler:: AddressMode u, QRhiSampler::AddressMode v) override;
+
+ QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
+ QRhiTextureRenderTarget::Flags flags) override;
+
+ QRhiSwapChain *createSwapChain() override;
+ QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
+ QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
+ QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override;
+ QRhi::FrameOpResult endOffscreenFrame() override;
+ QRhi::FrameOpResult finish() override;
+
+ void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+
+ void beginPass(QRhiCommandBuffer *cb,
+ QRhiRenderTarget *rt,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ QRhiResourceUpdateBatch *resourceUpdates) override;
+ void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+
+ void setGraphicsPipeline(QRhiCommandBuffer *cb,
+ QRhiGraphicsPipeline *ps) override;
+
+ void setShaderResources(QRhiCommandBuffer *cb,
+ QRhiShaderResourceBindings *srb,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) override;
+
+ void setVertexInput(QRhiCommandBuffer *cb,
+ int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
+ QRhiBuffer *indexBuf, quint32 indexOffset,
+ QRhiCommandBuffer::IndexFormat indexFormat) override;
+
+ void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) override;
+ void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override;
+ void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
+ void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
+
+ void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
+ quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
+
+ void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
+ quint32 instanceCount, quint32 firstIndex,
+ qint32 vertexOffset, quint32 firstInstance) override;
+
+ void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) override;
+ void debugMarkEnd(QRhiCommandBuffer *cb) override;
+ void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override;
+
+ void beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+ void endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+ void setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) override;
+ void dispatch(QRhiCommandBuffer *cb, int x, int y, int z) override;
+
+ const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) override;
+ void beginExternal(QRhiCommandBuffer *cb) override;
+ void endExternal(QRhiCommandBuffer *cb) override;
+
+ QVector<int> supportedSampleCounts() const override;
+ int ubufAlignment() const override;
+ bool isYUpInFramebuffer() const override;
+ bool isYUpInNDC() const override;
+ bool isClipDepthZeroToOne() const override;
+ QMatrix4x4 clipSpaceCorrMatrix() const override;
+ bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const override;
+ bool isFeatureSupported(QRhi::Feature feature) const override;
+ int resourceLimit(QRhi::ResourceLimit limit) const override;
+ const QRhiNativeHandles *nativeHandles() override;
+ void sendVMemStatsToProfiler() override;
+ void makeThreadLocalNativeContextCurrent() override;
+
+ void executeDeferredReleases(bool forced = false);
+ void finishActiveReadbacks(bool forced = false);
+ qsizetype subresUploadByteSize(const QRhiTextureSubresourceUploadDescription &subresDesc) const;
+ void enqueueSubresUpload(QMetalTexture *texD, void *mp, void *blitEncPtr,
+ int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc,
+ qsizetype *curOfs);
+ void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates);
+ void executeBufferHostWritesForCurrentFrame(QMetalBuffer *bufD);
+ void enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD, QMetalCommandBuffer *cbD,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets,
+ bool offsetOnlyChange);
+ int effectiveSampleCount(int sampleCount) const;
+
+ bool importedDevice = false;
+ bool importedCmdQueue = false;
+ QMetalSwapChain *currentSwapChain = nullptr;
+ QSet<QMetalSwapChain *> swapchains;
+ QRhiMetalNativeHandles nativeHandlesStruct;
+
+ struct {
+ int maxTextureSize = 4096;
+ } caps;
+
+ QRhiMetalData *d = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhinull.cpp b/src/gui/rhi/qrhinull.cpp
new file mode 100644
index 0000000000..1314e53893
--- /dev/null
+++ b/src/gui/rhi/qrhinull.cpp
@@ -0,0 +1,764 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qrhinull_p_p.h"
+#include <qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QRhiNullInitParams
+ \inmodule QtRhi
+ \brief Null backend specific initialization parameters.
+
+ A Null QRhi needs no special parameters for initialization.
+
+ \badcode
+ QRhiNullInitParams params;
+ rhi = QRhi::create(QRhi::Null, &params);
+ \endcode
+
+ The Null backend does not issue any graphics calls and creates no
+ resources. All QRhi operations will succeed as normal so applications can
+ still be run, albeit potentially at an unthrottled speed, depending on
+ their frame rendering strategy. The backend reports resources to
+ QRhiProfiler as usual.
+ */
+
+/*!
+ \class QRhiNullNativeHandles
+ \inmodule QtRhi
+ \brief Empty.
+ */
+
+/*!
+ \class QRhiNullTextureNativeHandles
+ \inmodule QtRhi
+ \brief Empty.
+ */
+
+QRhiNull::QRhiNull(QRhiNullInitParams *params)
+ : offscreenCommandBuffer(this)
+{
+ Q_UNUSED(params);
+}
+
+bool QRhiNull::create(QRhi::Flags flags)
+{
+ Q_UNUSED(flags);
+ return true;
+}
+
+void QRhiNull::destroy()
+{
+}
+
+QVector<int> QRhiNull::supportedSampleCounts() const
+{
+ return { 1 };
+}
+
+QRhiSwapChain *QRhiNull::createSwapChain()
+{
+ return new QNullSwapChain(this);
+}
+
+QRhiBuffer *QRhiNull::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size)
+{
+ return new QNullBuffer(this, type, usage, size);
+}
+
+int QRhiNull::ubufAlignment() const
+{
+ return 256;
+}
+
+bool QRhiNull::isYUpInFramebuffer() const
+{
+ return false;
+}
+
+bool QRhiNull::isYUpInNDC() const
+{
+ return true;
+}
+
+bool QRhiNull::isClipDepthZeroToOne() const
+{
+ return true;
+}
+
+QMatrix4x4 QRhiNull::clipSpaceCorrMatrix() const
+{
+ return QMatrix4x4(); // identity
+}
+
+bool QRhiNull::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const
+{
+ Q_UNUSED(format);
+ Q_UNUSED(flags);
+ return true;
+}
+
+bool QRhiNull::isFeatureSupported(QRhi::Feature feature) const
+{
+ Q_UNUSED(feature);
+ return true;
+}
+
+int QRhiNull::resourceLimit(QRhi::ResourceLimit limit) const
+{
+ switch (limit) {
+ case QRhi::TextureSizeMin:
+ return 1;
+ case QRhi::TextureSizeMax:
+ return 16384;
+ case QRhi::MaxColorAttachments:
+ return 8;
+ case QRhi::FramesInFlight:
+ return 2; // dummy
+ default:
+ Q_UNREACHABLE();
+ return 0;
+ }
+}
+
+const QRhiNativeHandles *QRhiNull::nativeHandles()
+{
+ return &nativeHandlesStruct;
+}
+
+void QRhiNull::sendVMemStatsToProfiler()
+{
+ // nothing to do here
+}
+
+void QRhiNull::makeThreadLocalNativeContextCurrent()
+{
+ // nothing to do here
+}
+
+QRhiRenderBuffer *QRhiNull::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags)
+{
+ return new QNullRenderBuffer(this, type, pixelSize, sampleCount, flags);
+}
+
+QRhiTexture *QRhiNull::createTexture(QRhiTexture::Format format, const QSize &pixelSize,
+ int sampleCount, QRhiTexture::Flags flags)
+{
+ return new QNullTexture(this, format, pixelSize, sampleCount, flags);
+}
+
+QRhiSampler *QRhiNull::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler::AddressMode u, QRhiSampler::AddressMode v)
+{
+ return new QNullSampler(this, magFilter, minFilter, mipmapMode, u, v);
+}
+
+QRhiTextureRenderTarget *QRhiNull::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
+ QRhiTextureRenderTarget::Flags flags)
+{
+ return new QNullTextureRenderTarget(this, desc, flags);
+}
+
+QRhiGraphicsPipeline *QRhiNull::createGraphicsPipeline()
+{
+ return new QNullGraphicsPipeline(this);
+}
+
+QRhiComputePipeline *QRhiNull::createComputePipeline()
+{
+ return new QNullComputePipeline(this);
+}
+
+QRhiShaderResourceBindings *QRhiNull::createShaderResourceBindings()
+{
+ return new QNullShaderResourceBindings(this);
+}
+
+void QRhiNull::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps)
+{
+ Q_UNUSED(cb);
+ Q_UNUSED(ps);
+}
+
+void QRhiNull::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
+{
+ Q_UNUSED(cb);
+ Q_UNUSED(srb);
+ Q_UNUSED(dynamicOffsetCount);
+ Q_UNUSED(dynamicOffsets);
+}
+
+void QRhiNull::setVertexInput(QRhiCommandBuffer *cb,
+ int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
+ QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
+{
+ Q_UNUSED(cb);
+ Q_UNUSED(startBinding);
+ Q_UNUSED(bindingCount);
+ Q_UNUSED(bindings);
+ Q_UNUSED(indexBuf);
+ Q_UNUSED(indexOffset);
+ Q_UNUSED(indexFormat);
+}
+
+void QRhiNull::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport)
+{
+ Q_UNUSED(cb);
+ Q_UNUSED(viewport);
+}
+
+void QRhiNull::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor)
+{
+ Q_UNUSED(cb);
+ Q_UNUSED(scissor);
+}
+
+void QRhiNull::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
+{
+ Q_UNUSED(cb);
+ Q_UNUSED(c);
+}
+
+void QRhiNull::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
+{
+ Q_UNUSED(cb);
+ Q_UNUSED(refValue);
+}
+
+void QRhiNull::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
+ quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
+{
+ Q_UNUSED(cb);
+ Q_UNUSED(vertexCount);
+ Q_UNUSED(instanceCount);
+ Q_UNUSED(firstVertex);
+ Q_UNUSED(firstInstance);
+}
+
+void QRhiNull::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
+ quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
+{
+ Q_UNUSED(cb);
+ Q_UNUSED(indexCount);
+ Q_UNUSED(instanceCount);
+ Q_UNUSED(firstIndex);
+ Q_UNUSED(vertexOffset);
+ Q_UNUSED(firstInstance);
+}
+
+void QRhiNull::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
+{
+ Q_UNUSED(cb);
+ Q_UNUSED(name);
+}
+
+void QRhiNull::debugMarkEnd(QRhiCommandBuffer *cb)
+{
+ Q_UNUSED(cb);
+}
+
+void QRhiNull::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
+{
+ Q_UNUSED(cb);
+ Q_UNUSED(msg);
+}
+
+void QRhiNull::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps)
+{
+ Q_UNUSED(cb);
+ Q_UNUSED(ps);
+}
+
+void QRhiNull::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
+{
+ Q_UNUSED(cb);
+ Q_UNUSED(x);
+ Q_UNUSED(y);
+ Q_UNUSED(z);
+}
+
+const QRhiNativeHandles *QRhiNull::nativeHandles(QRhiCommandBuffer *cb)
+{
+ Q_UNUSED(cb);
+ return nullptr;
+}
+
+void QRhiNull::beginExternal(QRhiCommandBuffer *cb)
+{
+ Q_UNUSED(cb);
+}
+
+void QRhiNull::endExternal(QRhiCommandBuffer *cb)
+{
+ Q_UNUSED(cb);
+}
+
+QRhi::FrameOpResult QRhiNull::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags)
+{
+ Q_UNUSED(flags);
+ currentSwapChain = swapChain;
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+ QRHI_PROF_F(beginSwapChainFrame(swapChain));
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiNull::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags)
+{
+ Q_UNUSED(flags);
+ QNullSwapChain *swapChainD = QRHI_RES(QNullSwapChain, swapChain);
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+ QRHI_PROF_F(endSwapChainFrame(swapChain, swapChainD->frameCount + 1));
+ QRHI_PROF_F(swapChainFrameGpuTime(swapChain, 0.000666f));
+ swapChainD->frameCount += 1;
+ currentSwapChain = nullptr;
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiNull::beginOffscreenFrame(QRhiCommandBuffer **cb)
+{
+ *cb = &offscreenCommandBuffer;
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiNull::endOffscreenFrame()
+{
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiNull::finish()
+{
+ return QRhi::FrameOpSuccess;
+}
+
+void QRhiNull::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_UNUSED(cb);
+ QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
+ for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) {
+ if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
+ QRhiReadbackResult *result = u.read.result;
+ QRhiTexture *tex = u.read.rb.texture();
+ if (tex) {
+ result->format = tex->format();
+ result->pixelSize = q->sizeForMipLevel(u.read.rb.level(), tex->pixelSize());
+ } else {
+ Q_ASSERT(currentSwapChain);
+ result->format = QRhiTexture::RGBA8;
+ result->pixelSize = currentSwapChain->currentPixelSize();
+ }
+ quint32 byteSize = 0;
+ textureFormatInfo(result->format, result->pixelSize, nullptr, &byteSize);
+ result->data.fill(0, byteSize);
+ if (result->completed)
+ result->completed();
+ }
+ }
+ ud->free();
+}
+
+void QRhiNull::beginPass(QRhiCommandBuffer *cb,
+ QRhiRenderTarget *rt,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ QRhiResourceUpdateBatch *resourceUpdates)
+{
+ Q_UNUSED(rt);
+ Q_UNUSED(colorClearValue);
+ Q_UNUSED(depthStencilClearValue);
+ if (resourceUpdates)
+ resourceUpdate(cb, resourceUpdates);
+}
+
+void QRhiNull::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ if (resourceUpdates)
+ resourceUpdate(cb, resourceUpdates);
+}
+
+void QRhiNull::beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ if (resourceUpdates)
+ resourceUpdate(cb, resourceUpdates);
+}
+
+void QRhiNull::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ if (resourceUpdates)
+ resourceUpdate(cb, resourceUpdates);
+}
+
+QNullBuffer::QNullBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size)
+ : QRhiBuffer(rhi, type, usage, size)
+{
+}
+
+QNullBuffer::~QNullBuffer()
+{
+ release();
+}
+
+void QNullBuffer::release()
+{
+ QRHI_PROF;
+ QRHI_PROF_F(releaseBuffer(this));
+}
+
+bool QNullBuffer::build()
+{
+ QRHI_PROF;
+ QRHI_PROF_F(newBuffer(this, m_size, 1, 0));
+ return true;
+}
+
+QNullRenderBuffer::QNullRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags)
+ : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags)
+{
+}
+
+QNullRenderBuffer::~QNullRenderBuffer()
+{
+ release();
+}
+
+void QNullRenderBuffer::release()
+{
+ QRHI_PROF;
+ QRHI_PROF_F(releaseRenderBuffer(this));
+}
+
+bool QNullRenderBuffer::build()
+{
+ QRHI_PROF;
+ QRHI_PROF_F(newRenderBuffer(this, false, false, 1));
+ return true;
+}
+
+QRhiTexture::Format QNullRenderBuffer::backingFormat() const
+{
+ return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
+}
+
+QNullTexture::QNullTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+ int sampleCount, Flags flags)
+ : QRhiTexture(rhi, format, pixelSize, sampleCount, flags)
+{
+}
+
+QNullTexture::~QNullTexture()
+{
+ release();
+}
+
+void QNullTexture::release()
+{
+ QRHI_PROF;
+ QRHI_PROF_F(releaseTexture(this));
+}
+
+bool QNullTexture::build()
+{
+ const bool isCube = m_flags.testFlag(CubeMap);
+ const bool hasMipMaps = m_flags.testFlag(MipMapped);
+ QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
+ const int mipLevelCount = hasMipMaps ? qCeil(log2(qMax(size.width(), size.height()))) + 1 : 1;
+ QRHI_PROF;
+ QRHI_PROF_F(newTexture(this, true, mipLevelCount, isCube ? 6 : 1, 1));
+ return true;
+}
+
+bool QNullTexture::buildFrom(const QRhiNativeHandles *src)
+{
+ Q_UNUSED(src);
+ const bool isCube = m_flags.testFlag(CubeMap);
+ const bool hasMipMaps = m_flags.testFlag(MipMapped);
+ QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
+ const int mipLevelCount = hasMipMaps ? qCeil(log2(qMax(size.width(), size.height()))) + 1 : 1;
+ QRHI_PROF;
+ QRHI_PROF_F(newTexture(this, false, mipLevelCount, isCube ? 6 : 1, 1));
+ return true;
+}
+
+const QRhiNativeHandles *QNullTexture::nativeHandles()
+{
+ return &nativeHandlesStruct;
+}
+
+QNullSampler::QNullSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
+ AddressMode u, AddressMode v)
+ : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v)
+{
+}
+
+QNullSampler::~QNullSampler()
+{
+ release();
+}
+
+void QNullSampler::release()
+{
+}
+
+bool QNullSampler::build()
+{
+ return true;
+}
+
+QNullRenderPassDescriptor::QNullRenderPassDescriptor(QRhiImplementation *rhi)
+ : QRhiRenderPassDescriptor(rhi)
+{
+}
+
+QNullRenderPassDescriptor::~QNullRenderPassDescriptor()
+{
+ release();
+}
+
+void QNullRenderPassDescriptor::release()
+{
+}
+
+QNullReferenceRenderTarget::QNullReferenceRenderTarget(QRhiImplementation *rhi)
+ : QRhiRenderTarget(rhi),
+ d(rhi)
+{
+}
+
+QNullReferenceRenderTarget::~QNullReferenceRenderTarget()
+{
+ release();
+}
+
+void QNullReferenceRenderTarget::release()
+{
+}
+
+QSize QNullReferenceRenderTarget::pixelSize() const
+{
+ return d.pixelSize;
+}
+
+float QNullReferenceRenderTarget::devicePixelRatio() const
+{
+ return d.dpr;
+}
+
+int QNullReferenceRenderTarget::sampleCount() const
+{
+ return 1;
+}
+
+QNullTextureRenderTarget::QNullTextureRenderTarget(QRhiImplementation *rhi,
+ const QRhiTextureRenderTargetDescription &desc,
+ Flags flags)
+ : QRhiTextureRenderTarget(rhi, desc, flags),
+ d(rhi)
+{
+}
+
+QNullTextureRenderTarget::~QNullTextureRenderTarget()
+{
+ release();
+}
+
+void QNullTextureRenderTarget::release()
+{
+}
+
+QRhiRenderPassDescriptor *QNullTextureRenderTarget::newCompatibleRenderPassDescriptor()
+{
+ return new QNullRenderPassDescriptor(m_rhi);
+}
+
+bool QNullTextureRenderTarget::build()
+{
+ d.rp = QRHI_RES(QNullRenderPassDescriptor, m_renderPassDesc);
+ const QVector<QRhiColorAttachment> colorAttachments = m_desc.colorAttachments();
+ if (!colorAttachments.isEmpty()) {
+ QRhiTexture *tex = colorAttachments.first().texture();
+ QRhiRenderBuffer *rb = colorAttachments.first().renderBuffer();
+ d.pixelSize = tex ? tex->pixelSize() : rb->pixelSize();
+ } else if (m_desc.depthStencilBuffer()) {
+ d.pixelSize = m_desc.depthStencilBuffer()->pixelSize();
+ } else if (m_desc.depthTexture()) {
+ d.pixelSize = m_desc.depthTexture()->pixelSize();
+ }
+ return true;
+}
+
+QSize QNullTextureRenderTarget::pixelSize() const
+{
+ return d.pixelSize;
+}
+
+float QNullTextureRenderTarget::devicePixelRatio() const
+{
+ return d.dpr;
+}
+
+int QNullTextureRenderTarget::sampleCount() const
+{
+ return 1;
+}
+
+QNullShaderResourceBindings::QNullShaderResourceBindings(QRhiImplementation *rhi)
+ : QRhiShaderResourceBindings(rhi)
+{
+}
+
+QNullShaderResourceBindings::~QNullShaderResourceBindings()
+{
+ release();
+}
+
+void QNullShaderResourceBindings::release()
+{
+}
+
+bool QNullShaderResourceBindings::build()
+{
+ return true;
+}
+
+QNullGraphicsPipeline::QNullGraphicsPipeline(QRhiImplementation *rhi)
+ : QRhiGraphicsPipeline(rhi)
+{
+}
+
+QNullGraphicsPipeline::~QNullGraphicsPipeline()
+{
+ release();
+}
+
+void QNullGraphicsPipeline::release()
+{
+}
+
+bool QNullGraphicsPipeline::build()
+{
+ return true;
+}
+
+QNullComputePipeline::QNullComputePipeline(QRhiImplementation *rhi)
+ : QRhiComputePipeline(rhi)
+{
+}
+
+QNullComputePipeline::~QNullComputePipeline()
+{
+ release();
+}
+
+void QNullComputePipeline::release()
+{
+}
+
+bool QNullComputePipeline::build()
+{
+ return true;
+}
+
+QNullCommandBuffer::QNullCommandBuffer(QRhiImplementation *rhi)
+ : QRhiCommandBuffer(rhi)
+{
+}
+
+QNullCommandBuffer::~QNullCommandBuffer()
+{
+ release();
+}
+
+void QNullCommandBuffer::release()
+{
+ // nothing to do here
+}
+
+QNullSwapChain::QNullSwapChain(QRhiImplementation *rhi)
+ : QRhiSwapChain(rhi),
+ rt(rhi),
+ cb(rhi)
+{
+}
+
+QNullSwapChain::~QNullSwapChain()
+{
+ release();
+}
+
+void QNullSwapChain::release()
+{
+ QRHI_PROF;
+ QRHI_PROF_F(releaseSwapChain(this));
+}
+
+QRhiCommandBuffer *QNullSwapChain::currentFrameCommandBuffer()
+{
+ return &cb;
+}
+
+QRhiRenderTarget *QNullSwapChain::currentFrameRenderTarget()
+{
+ return &rt;
+}
+
+QSize QNullSwapChain::surfacePixelSize()
+{
+ return QSize(1280, 720);
+}
+
+QRhiRenderPassDescriptor *QNullSwapChain::newCompatibleRenderPassDescriptor()
+{
+ return new QNullRenderPassDescriptor(m_rhi);
+}
+
+bool QNullSwapChain::buildOrResize()
+{
+ m_currentPixelSize = surfacePixelSize();
+ rt.d.rp = QRHI_RES(QNullRenderPassDescriptor, m_renderPassDesc);
+ rt.d.pixelSize = m_currentPixelSize;
+ frameCount = 0;
+ QRHI_PROF;
+ QRHI_PROF_F(resizeSwapChain(this, 1, 0, 1));
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/rhi/qrhinull_p.h b/src/gui/rhi/qrhinull_p.h
new file mode 100644
index 0000000000..7d3ce5dbf1
--- /dev/null
+++ b/src/gui/rhi/qrhinull_p.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHINULL_H
+#define QRHINULL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qrhi_p.h>
+
+QT_BEGIN_NAMESPACE
+
+struct Q_GUI_EXPORT QRhiNullInitParams : public QRhiInitParams
+{
+};
+
+struct Q_GUI_EXPORT QRhiNullNativeHandles : public QRhiNativeHandles
+{
+};
+
+struct Q_GUI_EXPORT QRhiNullTextureNativeHandles : public QRhiNativeHandles
+{
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhinull_p_p.h b/src/gui/rhi/qrhinull_p_p.h
new file mode 100644
index 0000000000..b0227bc110
--- /dev/null
+++ b/src/gui/rhi/qrhinull_p_p.h
@@ -0,0 +1,294 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHINULL_P_H
+#define QRHINULL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qrhinull_p.h"
+#include "qrhi_p_p.h"
+
+QT_BEGIN_NAMESPACE
+
+struct QNullBuffer : public QRhiBuffer
+{
+ QNullBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size);
+ ~QNullBuffer();
+ void release() override;
+ bool build() override;
+};
+
+struct QNullRenderBuffer : public QRhiRenderBuffer
+{
+ QNullRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags);
+ ~QNullRenderBuffer();
+ void release() override;
+ bool build() override;
+ QRhiTexture::Format backingFormat() const override;
+};
+
+struct QNullTexture : public QRhiTexture
+{
+ QNullTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+ int sampleCount, Flags flags);
+ ~QNullTexture();
+ void release() override;
+ bool build() override;
+ bool buildFrom(const QRhiNativeHandles *src) override;
+ const QRhiNativeHandles *nativeHandles() override;
+
+ QRhiNullTextureNativeHandles nativeHandlesStruct;
+};
+
+struct QNullSampler : public QRhiSampler
+{
+ QNullSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
+ AddressMode u, AddressMode v);
+ ~QNullSampler();
+ void release() override;
+ bool build() override;
+};
+
+struct QNullRenderPassDescriptor : public QRhiRenderPassDescriptor
+{
+ QNullRenderPassDescriptor(QRhiImplementation *rhi);
+ ~QNullRenderPassDescriptor();
+ void release() override;
+};
+
+struct QNullRenderTargetData
+{
+ QNullRenderTargetData(QRhiImplementation *) { }
+
+ QNullRenderPassDescriptor *rp = nullptr;
+ QSize pixelSize;
+ float dpr = 1;
+};
+
+struct QNullReferenceRenderTarget : public QRhiRenderTarget
+{
+ QNullReferenceRenderTarget(QRhiImplementation *rhi);
+ ~QNullReferenceRenderTarget();
+ void release() override;
+
+ QSize pixelSize() const override;
+ float devicePixelRatio() const override;
+ int sampleCount() const override;
+
+ QNullRenderTargetData d;
+};
+
+struct QNullTextureRenderTarget : public QRhiTextureRenderTarget
+{
+ QNullTextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc, Flags flags);
+ ~QNullTextureRenderTarget();
+ void release() override;
+
+ QSize pixelSize() const override;
+ float devicePixelRatio() const override;
+ int sampleCount() const override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+ bool build() override;
+
+ QNullRenderTargetData d;
+};
+
+struct QNullShaderResourceBindings : public QRhiShaderResourceBindings
+{
+ QNullShaderResourceBindings(QRhiImplementation *rhi);
+ ~QNullShaderResourceBindings();
+ void release() override;
+ bool build() override;
+};
+
+struct QNullGraphicsPipeline : public QRhiGraphicsPipeline
+{
+ QNullGraphicsPipeline(QRhiImplementation *rhi);
+ ~QNullGraphicsPipeline();
+ void release() override;
+ bool build() override;
+};
+
+struct QNullComputePipeline : public QRhiComputePipeline
+{
+ QNullComputePipeline(QRhiImplementation *rhi);
+ ~QNullComputePipeline();
+ void release() override;
+ bool build() override;
+};
+
+struct QNullCommandBuffer : public QRhiCommandBuffer
+{
+ QNullCommandBuffer(QRhiImplementation *rhi);
+ ~QNullCommandBuffer();
+ void release() override;
+};
+
+struct QNullSwapChain : public QRhiSwapChain
+{
+ QNullSwapChain(QRhiImplementation *rhi);
+ ~QNullSwapChain();
+ void release() override;
+
+ QRhiCommandBuffer *currentFrameCommandBuffer() override;
+ QRhiRenderTarget *currentFrameRenderTarget() override;
+
+ QSize surfacePixelSize() override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+ bool buildOrResize() override;
+
+ QNullReferenceRenderTarget rt;
+ QNullCommandBuffer cb;
+ int frameCount = 0;
+};
+
+class QRhiNull : public QRhiImplementation
+{
+public:
+ QRhiNull(QRhiNullInitParams *params);
+
+ bool create(QRhi::Flags flags) override;
+ void destroy() override;
+
+ QRhiGraphicsPipeline *createGraphicsPipeline() override;
+ QRhiComputePipeline *createComputePipeline() override;
+ QRhiShaderResourceBindings *createShaderResourceBindings() override;
+ QRhiBuffer *createBuffer(QRhiBuffer::Type type,
+ QRhiBuffer::UsageFlags usage,
+ int size) override;
+ QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiRenderBuffer::Flags flags) override;
+ QRhiTexture *createTexture(QRhiTexture::Format format,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiTexture::Flags flags) override;
+ QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler:: AddressMode u, QRhiSampler::AddressMode v) override;
+
+ QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
+ QRhiTextureRenderTarget::Flags flags) override;
+
+ QRhiSwapChain *createSwapChain() override;
+ QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
+ QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
+ QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override;
+ QRhi::FrameOpResult endOffscreenFrame() override;
+ QRhi::FrameOpResult finish() override;
+
+ void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+
+ void beginPass(QRhiCommandBuffer *cb,
+ QRhiRenderTarget *rt,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ QRhiResourceUpdateBatch *resourceUpdates) override;
+ void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+
+ void setGraphicsPipeline(QRhiCommandBuffer *cb,
+ QRhiGraphicsPipeline *ps) override;
+
+ void setShaderResources(QRhiCommandBuffer *cb,
+ QRhiShaderResourceBindings *srb,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) override;
+
+ void setVertexInput(QRhiCommandBuffer *cb,
+ int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
+ QRhiBuffer *indexBuf, quint32 indexOffset,
+ QRhiCommandBuffer::IndexFormat indexFormat) override;
+
+ void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) override;
+ void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override;
+ void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
+ void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
+
+ void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
+ quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
+
+ void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
+ quint32 instanceCount, quint32 firstIndex,
+ qint32 vertexOffset, quint32 firstInstance) override;
+
+ void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) override;
+ void debugMarkEnd(QRhiCommandBuffer *cb) override;
+ void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override;
+
+ void beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+ void endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+ void setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) override;
+ void dispatch(QRhiCommandBuffer *cb, int x, int y, int z) override;
+
+ const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) override;
+ void beginExternal(QRhiCommandBuffer *cb) override;
+ void endExternal(QRhiCommandBuffer *cb) override;
+
+ QVector<int> supportedSampleCounts() const override;
+ int ubufAlignment() const override;
+ bool isYUpInFramebuffer() const override;
+ bool isYUpInNDC() const override;
+ bool isClipDepthZeroToOne() const override;
+ QMatrix4x4 clipSpaceCorrMatrix() const override;
+ bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const override;
+ bool isFeatureSupported(QRhi::Feature feature) const override;
+ int resourceLimit(QRhi::ResourceLimit limit) const override;
+ const QRhiNativeHandles *nativeHandles() override;
+ void sendVMemStatsToProfiler() override;
+ void makeThreadLocalNativeContextCurrent() override;
+
+ QRhiNullNativeHandles nativeHandlesStruct;
+ QRhiSwapChain *currentSwapChain = nullptr;
+ QNullCommandBuffer offscreenCommandBuffer;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhiprofiler.cpp b/src/gui/rhi/qrhiprofiler.cpp
new file mode 100644
index 0000000000..15e3007d49
--- /dev/null
+++ b/src/gui/rhi/qrhiprofiler.cpp
@@ -0,0 +1,603 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qrhiprofiler_p_p.h"
+#include "qrhi_p_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QRhiProfiler
+ \inmodule QtRhi
+
+ \brief Collects resource and timing information from an active QRhi.
+
+ A QRhiProfiler is present for each QRhi. Query it via QRhi::profiler(). The
+ profiler is active only when the QRhi was created with
+ QRhi::EnableProfiling. No data is collected otherwise.
+
+ \note GPU timings are only available when QRhi indicates that
+ QRhi::Timestamps is supported.
+
+ Besides collecting data from the QRhi implementations, some additional
+ values are calculated. For example, for textures and similar resources the
+ profiler gives an estimate of the complete amount of memory the resource
+ needs.
+
+ \section2 Output Format
+
+ The output is comma-separated text. Each line has a number of
+ comma-separated entries and each line ends with a comma.
+
+ For example:
+
+ \badcode
+ 1,0,140446057946208,Triangle vbuf,type,0,usage,1,logical_size,84,effective_size,84,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
+ 1,0,140446057947376,Triangle ubuf,type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0,
+ 1,1,140446057950416,,type,0,usage,1,logical_size,112,effective_size,112,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
+ 1,1,140446057950544,,type,0,usage,2,logical_size,12,effective_size,12,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
+ 1,1,140446057947440,,type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0,
+ 1,1,140446057984784,Cube vbuf (textured),type,0,usage,1,logical_size,720,effective_size,720,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
+ 1,1,140446057982528,Cube ubuf (textured),type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0,
+ 7,8,140446058913648,Qt texture,width,256,height,256,format,1,owns_native_resource,1,mip_count,9,layer_count,1,effective_sample_count,1,approx_byte_size,349524,
+ 1,8,140446058795856,Cube vbuf (textured with offscreen),type,0,usage,1,logical_size,720,effective_size,720,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
+ 1,8,140446058947920,Cube ubuf (textured with offscreen),type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0,
+ 7,8,140446058794928,Texture for offscreen content,width,512,height,512,format,1,owns_native_resource,1,mip_count,1,layer_count,1,effective_sample_count,1,approx_byte_size,1048576,
+ 1,8,140446058963904,Triangle vbuf,type,0,usage,1,logical_size,84,effective_size,84,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
+ 1,8,140446058964560,Triangle ubuf,type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0,
+ 5,9,140446057945392,,type,0,width,1280,height,720,effective_sample_count,1,transient_backing,0,winsys_backing,0,approx_byte_size,3686400,
+ 11,9,140446057944592,,width,1280,height,720,buffer_count,2,msaa_buffer_count,0,effective_sample_count,1,approx_total_byte_size,7372800,
+ 9,9,140446058913648,Qt texture,slot,0,size,262144,
+ 10,9,140446058913648,Qt texture,slot,0,
+ 17,2019,140446057944592,,frames_since_resize,121,min_ms_frame_delta,9,max_ms_frame_delta,33,Favg_ms_frame_delta,16.1167,
+ 18,2019,140446057944592,,frames_since_resize,121,min_ms_frame_build,0,max_ms_frame_build,1,Favg_ms_frame_build,0.00833333,
+ 17,4019,140446057944592,,frames_since_resize,241,min_ms_frame_delta,15,max_ms_frame_delta,17,Favg_ms_frame_delta,16.0583,
+ 18,4019,140446057944592,,frames_since_resize,241,min_ms_frame_build,0,max_ms_frame_build,0,Favg_ms_frame_build,0,
+ 12,5070,140446057944592,,
+ 2,5079,140446057947376,Triangle ubuf,
+ 2,5079,140446057946208,Triangle vbuf,
+ 2,5079,140446057947440,,
+ 2,5079,140446057950544,,
+ 2,5079,140446057950416,,
+ 8,5079,140446058913648,Qt texture,
+ 2,5079,140446057982528,Cube ubuf (textured),
+ 2,5079,140446057984784,Cube vbuf (textured),
+ 2,5079,140446058964560,Triangle ubuf,
+ 2,5079,140446058963904,Triangle vbuf,
+ 8,5079,140446058794928,Texture for offscreen content,
+ 2,5079,140446058947920,Cube ubuf (textured with offscreen),
+ 2,5079,140446058795856,Cube vbuf (textured with offscreen),
+ 6,5079,140446057945392,,
+ \endcode
+
+ Each line starts with \c op, \c timestamp, \c res, \c name where op is a
+ value from StreamOp, timestamp is a recording timestamp in milliseconds
+ (qint64), res is a number (quint64) referring to the QRhiResource the entry
+ refers to, or 0 if not applicable. \c name is the value of
+ QRhiResource::name() and may be empty as well. The \c name will never
+ contain a comma.
+
+ This is followed by any number of \c{key, value} pairs where \c key is an
+ unspecified string and \c value is a number. If \c key starts with \c F, it
+ indicates the value is a float. Otherwise assume that the value is a
+ qint64.
+ */
+
+/*!
+ \enum QRhiProfiler::StreamOp
+ Describes an entry in the profiler's output stream.
+
+ \value NewBuffer A buffer is created
+ \value ReleaseBuffer A buffer is destroyed
+ \value NewBufferStagingArea A staging buffer for buffer upload is created
+ \value ReleaseBufferStagingArea A staging buffer for buffer upload is destroyed
+ \value NewRenderBuffer A renderbuffer is created
+ \value ReleaseRenderBuffer A renderbuffer is destroyed
+ \value NewTexture A texture is created
+ \value ReleaseTexture A texture is destroyed
+ \value NewTextureStagingArea A staging buffer for texture upload is created
+ \value ReleaseTextureStagingArea A staging buffer for texture upload is destroyed
+ \value ResizeSwapChain A swapchain is created or resized
+ \value ReleaseSwapChain A swapchain is destroyed
+ \value NewReadbackBuffer A staging buffer for readback is created
+ \value ReleaseReadbackBuffer A staging buffer for readback is destroyed
+ \value GpuMemAllocStats GPU memory allocator statistics
+ \value GpuFrameTime GPU frame times
+ \value FrameToFrameTime CPU frame-to-frame times
+ \value FrameBuildTime CPU beginFrame-endFrame times
+ */
+
+/*!
+ \class QRhiProfiler::CpuTime
+ \inmodule QtRhi
+ \brief Contains CPU-side frame timings.
+
+ Once sufficient number of frames have been rendered, the minimum, maximum,
+ and average values (in milliseconds) from various measurements are made
+ available in this struct queriable from QRhiProfiler::frameToFrameTimes()
+ and QRhiProfiler::frameBuildTimes().
+
+ \sa QRhiProfiler::setFrameTimingWriteInterval()
+ */
+
+/*!
+ \class QRhiProfiler::GpuTime
+ \inmodule QtRhi
+ \brief Contains GPU-side frame timings.
+
+ Once sufficient number of frames have been rendered, the minimum, maximum,
+ and average values (in milliseconds) calculated from GPU command buffer
+ timestamps are made available in this struct queriable from
+ QRhiProfiler::gpuFrameTimes().
+
+ \sa QRhiProfiler::setFrameTimingWriteInterval()
+ */
+
+/*!
+ \internal
+ */
+QRhiProfiler::QRhiProfiler()
+ : d(new QRhiProfilerPrivate)
+{
+ d->ts.start();
+}
+
+/*!
+ Destructor.
+ */
+QRhiProfiler::~QRhiProfiler()
+{
+ // Flush because there is a high chance we have writes that were made since
+ // the event loop last ran. (esp. relevant for network devices like QTcpSocket)
+ if (d->outputDevice)
+ d->outputDevice->waitForBytesWritten(1000);
+
+ delete d;
+}
+
+/*!
+ Sets the output \a device.
+
+ \note No output will be generated when QRhi::EnableProfiling was not set.
+ */
+void QRhiProfiler::setDevice(QIODevice *device)
+{
+ d->outputDevice = device;
+}
+
+/*!
+ Requests writing a GpuMemAllocStats entry into the output, when applicable.
+ Backends that do not support this will ignore the request. This is an
+ explicit request since getting the allocator status and statistics may be
+ an expensive operation.
+ */
+void QRhiProfiler::addVMemAllocatorStats()
+{
+ if (d->rhiDWhenEnabled)
+ d->rhiDWhenEnabled->sendVMemStatsToProfiler();
+}
+
+/*!
+ \return the currently set frame timing writeout interval.
+ */
+int QRhiProfiler::frameTimingWriteInterval() const
+{
+ return d->frameTimingWriteInterval;
+}
+
+/*!
+ Sets the number of frames that need to be rendered before the collected CPU
+ and GPU timings are processed (min, max, average are calculated) to \a
+ frameCount.
+
+ The default value is 120.
+ */
+void QRhiProfiler::setFrameTimingWriteInterval(int frameCount)
+{
+ if (frameCount > 0)
+ d->frameTimingWriteInterval = frameCount;
+}
+
+/*!
+ \return min, max, and avg in milliseconds for the time that elapsed between two
+ QRhi::endFrame() calls.
+
+ \note The values are all 0 until at least frameTimingWriteInterval() frames
+ have been rendered.
+ */
+QRhiProfiler::CpuTime QRhiProfiler::frameToFrameTimes(QRhiSwapChain *sc) const
+{
+ auto it = d->swapchains.constFind(sc);
+ if (it != d->swapchains.constEnd())
+ return it->frameToFrameTime;
+
+ return QRhiProfiler::CpuTime();
+}
+
+/*!
+ \return min, max, and avg in milliseconds for the time that elapsed between
+ a QRhi::beginFrame() and QRhi::endFrame().
+
+ \note The values are all 0 until at least frameTimingWriteInterval() frames
+ have been rendered.
+ */
+QRhiProfiler::CpuTime QRhiProfiler::frameBuildTimes(QRhiSwapChain *sc) const
+{
+ auto it = d->swapchains.constFind(sc);
+ if (it != d->swapchains.constEnd())
+ return it->beginToEndFrameTime;
+
+ return QRhiProfiler::CpuTime();
+}
+
+/*!
+ \return min, max, and avg in milliseconds for the GPU time that is spent on
+ one frame.
+
+ \note The values are all 0 until at least frameTimingWriteInterval() frames
+ have been rendered.
+
+ The GPU times should only be compared between runs on the same GPU of the
+ same system with the same backend. Comparing times for different graphics
+ cards or for different backends can give misleading results. The numbers are
+ not meant to be comparable that way.
+
+ \note Some backends have no support for this, and even for those that have,
+ it is not guaranteed that the driver will support it at run time. Support
+ can be checked via QRhi::Timestamps.
+ */
+QRhiProfiler::GpuTime QRhiProfiler::gpuFrameTimes(QRhiSwapChain *sc) const
+{
+ auto it = d->swapchains.constFind(sc);
+ if (it != d->swapchains.constEnd())
+ return it->gpuFrameTime;
+
+ return QRhiProfiler::GpuTime();
+}
+
+void QRhiProfilerPrivate::startEntry(QRhiProfiler::StreamOp op, qint64 timestamp, QRhiResource *res)
+{
+ buf.clear();
+ buf.append(QByteArray::number(op));
+ buf.append(',');
+ buf.append(QByteArray::number(timestamp));
+ buf.append(',');
+ buf.append(QByteArray::number(quint64(quintptr(res))));
+ buf.append(',');
+ if (res)
+ buf.append(res->name());
+ buf.append(',');
+}
+
+void QRhiProfilerPrivate::writeInt(const char *key, qint64 v)
+{
+ Q_ASSERT(key[0] != 'F');
+ buf.append(key);
+ buf.append(',');
+ buf.append(QByteArray::number(v));
+ buf.append(',');
+}
+
+void QRhiProfilerPrivate::writeFloat(const char *key, float f)
+{
+ Q_ASSERT(key[0] == 'F');
+ buf.append(key);
+ buf.append(',');
+ buf.append(QByteArray::number(f));
+ buf.append(',');
+}
+
+void QRhiProfilerPrivate::endEntry()
+{
+ buf.append('\n');
+ outputDevice->write(buf);
+}
+
+void QRhiProfilerPrivate::newBuffer(QRhiBuffer *buf, quint32 realSize, int backingGpuBufCount, int backingCpuBufCount)
+{
+ if (!outputDevice)
+ return;
+
+ startEntry(QRhiProfiler::NewBuffer, ts.elapsed(), buf);
+ writeInt("type", buf->type());
+ writeInt("usage", buf->usage());
+ writeInt("logical_size", buf->size());
+ writeInt("effective_size", realSize);
+ writeInt("backing_gpu_buf_count", backingGpuBufCount);
+ writeInt("backing_cpu_buf_count", backingCpuBufCount);
+ endEntry();
+}
+
+void QRhiProfilerPrivate::releaseBuffer(QRhiBuffer *buf)
+{
+ if (!outputDevice)
+ return;
+
+ startEntry(QRhiProfiler::ReleaseBuffer, ts.elapsed(), buf);
+ endEntry();
+}
+
+void QRhiProfilerPrivate::newBufferStagingArea(QRhiBuffer *buf, int slot, quint32 size)
+{
+ if (!outputDevice)
+ return;
+
+ startEntry(QRhiProfiler::NewBufferStagingArea, ts.elapsed(), buf);
+ writeInt("slot", slot);
+ writeInt("size", size);
+ endEntry();
+}
+
+void QRhiProfilerPrivate::releaseBufferStagingArea(QRhiBuffer *buf, int slot)
+{
+ if (!outputDevice)
+ return;
+
+ startEntry(QRhiProfiler::ReleaseBufferStagingArea, ts.elapsed(), buf);
+ writeInt("slot", slot);
+ endEntry();
+}
+
+void QRhiProfilerPrivate::newRenderBuffer(QRhiRenderBuffer *rb, bool transientBacking, bool winSysBacking, int sampleCount)
+{
+ if (!outputDevice)
+ return;
+
+ const QRhiRenderBuffer::Type type = rb->type();
+ const QSize sz = rb->pixelSize();
+ // just make up something, ds is likely D24S8 while color is RGBA8 or similar
+ const QRhiTexture::Format assumedFormat = type == QRhiRenderBuffer::DepthStencil ? QRhiTexture::D32F : QRhiTexture::RGBA8;
+ quint32 byteSize = rhiDWhenEnabled->approxByteSizeForTexture(assumedFormat, sz, 1, 1);
+ if (sampleCount > 1)
+ byteSize *= sampleCount;
+
+ startEntry(QRhiProfiler::NewRenderBuffer, ts.elapsed(), rb);
+ writeInt("type", type);
+ writeInt("width", sz.width());
+ writeInt("height", sz.height());
+ writeInt("effective_sample_count", sampleCount);
+ writeInt("transient_backing", transientBacking);
+ writeInt("winsys_backing", winSysBacking);
+ writeInt("approx_byte_size", byteSize);
+ endEntry();
+}
+
+void QRhiProfilerPrivate::releaseRenderBuffer(QRhiRenderBuffer *rb)
+{
+ if (!outputDevice)
+ return;
+
+ startEntry(QRhiProfiler::ReleaseRenderBuffer, ts.elapsed(), rb);
+ endEntry();
+}
+
+void QRhiProfilerPrivate::newTexture(QRhiTexture *tex, bool owns, int mipCount, int layerCount, int sampleCount)
+{
+ if (!outputDevice)
+ return;
+
+ const QRhiTexture::Format format = tex->format();
+ const QSize sz = tex->pixelSize();
+ quint32 byteSize = rhiDWhenEnabled->approxByteSizeForTexture(format, sz, mipCount, layerCount);
+ if (sampleCount > 1)
+ byteSize *= sampleCount;
+
+ startEntry(QRhiProfiler::NewTexture, ts.elapsed(), tex);
+ writeInt("width", sz.width());
+ writeInt("height", sz.height());
+ writeInt("format", format);
+ writeInt("owns_native_resource", owns);
+ writeInt("mip_count", mipCount);
+ writeInt("layer_count", layerCount);
+ writeInt("effective_sample_count", sampleCount);
+ writeInt("approx_byte_size", byteSize);
+ endEntry();
+}
+
+void QRhiProfilerPrivate::releaseTexture(QRhiTexture *tex)
+{
+ if (!outputDevice)
+ return;
+
+ startEntry(QRhiProfiler::ReleaseTexture, ts.elapsed(), tex);
+ endEntry();
+}
+
+void QRhiProfilerPrivate::newTextureStagingArea(QRhiTexture *tex, int slot, quint32 size)
+{
+ if (!outputDevice)
+ return;
+
+ startEntry(QRhiProfiler::NewTextureStagingArea, ts.elapsed(), tex);
+ writeInt("slot", slot);
+ writeInt("size", size);
+ endEntry();
+}
+
+void QRhiProfilerPrivate::releaseTextureStagingArea(QRhiTexture *tex, int slot)
+{
+ if (!outputDevice)
+ return;
+
+ startEntry(QRhiProfiler::ReleaseTextureStagingArea, ts.elapsed(), tex);
+ writeInt("slot", slot);
+ endEntry();
+}
+
+void QRhiProfilerPrivate::resizeSwapChain(QRhiSwapChain *sc, int bufferCount, int msaaBufferCount, int sampleCount)
+{
+ if (!outputDevice)
+ return;
+
+ const QSize sz = sc->currentPixelSize();
+ quint32 byteSize = rhiDWhenEnabled->approxByteSizeForTexture(QRhiTexture::BGRA8, sz, 1, 1);
+ byteSize = byteSize * bufferCount + byteSize * msaaBufferCount * sampleCount;
+
+ startEntry(QRhiProfiler::ResizeSwapChain, ts.elapsed(), sc);
+ writeInt("width", sz.width());
+ writeInt("height", sz.height());
+ writeInt("buffer_count", bufferCount);
+ writeInt("msaa_buffer_count", msaaBufferCount);
+ writeInt("effective_sample_count", sampleCount);
+ writeInt("approx_total_byte_size", byteSize);
+ endEntry();
+}
+
+void QRhiProfilerPrivate::releaseSwapChain(QRhiSwapChain *sc)
+{
+ if (!outputDevice)
+ return;
+
+ startEntry(QRhiProfiler::ReleaseSwapChain, ts.elapsed(), sc);
+ endEntry();
+}
+
+template<typename T>
+void calcTiming(QVector<T> *vec, T *minDelta, T *maxDelta, float *avgDelta)
+{
+ if (vec->isEmpty())
+ return;
+
+ *minDelta = *maxDelta = 0;
+ float totalDelta = 0;
+ for (T delta : qAsConst(*vec)) {
+ totalDelta += float(delta);
+ if (*minDelta == 0 || delta < *minDelta)
+ *minDelta = delta;
+ if (*maxDelta == 0 || delta > *maxDelta)
+ *maxDelta = delta;
+ }
+ *avgDelta = totalDelta / vec->count();
+
+ vec->clear();
+}
+
+void QRhiProfilerPrivate::beginSwapChainFrame(QRhiSwapChain *sc)
+{
+ Sc &scd(swapchains[sc]);
+ scd.beginToEndTimer.start();
+}
+
+void QRhiProfilerPrivate::endSwapChainFrame(QRhiSwapChain *sc, int frameCount)
+{
+ Sc &scd(swapchains[sc]);
+ if (!scd.frameToFrameRunning) {
+ scd.frameToFrameTimer.start();
+ scd.frameToFrameRunning = true;
+ return;
+ }
+
+ scd.frameToFrameSamples.append(scd.frameToFrameTimer.restart());
+ if (scd.frameToFrameSamples.count() >= frameTimingWriteInterval) {
+ calcTiming(&scd.frameToFrameSamples,
+ &scd.frameToFrameTime.minTime, &scd.frameToFrameTime.maxTime, &scd.frameToFrameTime.avgTime);
+ if (outputDevice) {
+ startEntry(QRhiProfiler::FrameToFrameTime, ts.elapsed(), sc);
+ writeInt("frames_since_resize", frameCount);
+ writeInt("min_ms_frame_delta", scd.frameToFrameTime.minTime);
+ writeInt("max_ms_frame_delta", scd.frameToFrameTime.maxTime);
+ writeFloat("Favg_ms_frame_delta", scd.frameToFrameTime.avgTime);
+ endEntry();
+ }
+ }
+
+ scd.beginToEndSamples.append(scd.beginToEndTimer.elapsed());
+ if (scd.beginToEndSamples.count() >= frameTimingWriteInterval) {
+ calcTiming(&scd.beginToEndSamples,
+ &scd.beginToEndFrameTime.minTime, &scd.beginToEndFrameTime.maxTime, &scd.beginToEndFrameTime.avgTime);
+ if (outputDevice) {
+ startEntry(QRhiProfiler::FrameBuildTime, ts.elapsed(), sc);
+ writeInt("frames_since_resize", frameCount);
+ writeInt("min_ms_frame_build", scd.beginToEndFrameTime.minTime);
+ writeInt("max_ms_frame_build", scd.beginToEndFrameTime.maxTime);
+ writeFloat("Favg_ms_frame_build", scd.beginToEndFrameTime.avgTime);
+ endEntry();
+ }
+ }
+}
+
+void QRhiProfilerPrivate::swapChainFrameGpuTime(QRhiSwapChain *sc, float gpuTime)
+{
+ Sc &scd(swapchains[sc]);
+ scd.gpuFrameSamples.append(gpuTime);
+ if (scd.gpuFrameSamples.count() >= frameTimingWriteInterval) {
+ calcTiming(&scd.gpuFrameSamples,
+ &scd.gpuFrameTime.minTime, &scd.gpuFrameTime.maxTime, &scd.gpuFrameTime.avgTime);
+ if (outputDevice) {
+ startEntry(QRhiProfiler::GpuFrameTime, ts.elapsed(), sc);
+ writeFloat("Fmin_ms_gpu_frame_time", scd.gpuFrameTime.minTime);
+ writeFloat("Fmax_ms_gpu_frame_time", scd.gpuFrameTime.maxTime);
+ writeFloat("Favg_ms_gpu_frame_time", scd.gpuFrameTime.avgTime);
+ endEntry();
+ }
+ }
+}
+
+void QRhiProfilerPrivate::newReadbackBuffer(quint64 id, QRhiResource *src, quint32 size)
+{
+ if (!outputDevice)
+ return;
+
+ startEntry(QRhiProfiler::NewReadbackBuffer, ts.elapsed(), src);
+ writeInt("id", id);
+ writeInt("size", size);
+ endEntry();
+}
+
+void QRhiProfilerPrivate::releaseReadbackBuffer(quint64 id)
+{
+ if (!outputDevice)
+ return;
+
+ startEntry(QRhiProfiler::ReleaseReadbackBuffer, ts.elapsed(), nullptr);
+ writeInt("id", id);
+ endEntry();
+}
+
+void QRhiProfilerPrivate::vmemStat(int realAllocCount, int subAllocCount, quint32 totalSize, quint32 unusedSize)
+{
+ if (!outputDevice)
+ return;
+
+ startEntry(QRhiProfiler::GpuMemAllocStats, ts.elapsed(), nullptr);
+ writeInt("real_alloc_count", realAllocCount);
+ writeInt("sub_alloc_count", subAllocCount);
+ writeInt("total_size", totalSize);
+ writeInt("unused_size", unusedSize);
+ endEntry();
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/rhi/qrhiprofiler_p.h b/src/gui/rhi/qrhiprofiler_p.h
new file mode 100644
index 0000000000..89fd0a8798
--- /dev/null
+++ b/src/gui/rhi/qrhiprofiler_p.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHIPROFILER_H
+#define QRHIPROFILER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qrhi_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QRhiProfilerPrivate;
+class QIODevice;
+
+class Q_GUI_EXPORT QRhiProfiler
+{
+public:
+ enum StreamOp {
+ NewBuffer = 1,
+ ReleaseBuffer,
+ NewBufferStagingArea,
+ ReleaseBufferStagingArea,
+ NewRenderBuffer,
+ ReleaseRenderBuffer,
+ NewTexture,
+ ReleaseTexture,
+ NewTextureStagingArea,
+ ReleaseTextureStagingArea,
+ ResizeSwapChain,
+ ReleaseSwapChain,
+ NewReadbackBuffer,
+ ReleaseReadbackBuffer,
+ GpuMemAllocStats,
+ GpuFrameTime,
+ FrameToFrameTime,
+ FrameBuildTime
+ };
+
+ ~QRhiProfiler();
+
+ void setDevice(QIODevice *device);
+
+ void addVMemAllocatorStats();
+
+ int frameTimingWriteInterval() const;
+ void setFrameTimingWriteInterval(int frameCount);
+
+ struct CpuTime {
+ qint64 minTime = 0;
+ qint64 maxTime = 0;
+ float avgTime = 0;
+ };
+
+ struct GpuTime {
+ float minTime = 0;
+ float maxTime = 0;
+ float avgTime = 0;
+ };
+
+ CpuTime frameToFrameTimes(QRhiSwapChain *sc) const;
+ CpuTime frameBuildTimes(QRhiSwapChain *sc) const; // beginFrame - endFrame
+ GpuTime gpuFrameTimes(QRhiSwapChain *sc) const;
+
+private:
+ Q_DISABLE_COPY(QRhiProfiler)
+ QRhiProfiler();
+ QRhiProfilerPrivate *d;
+ friend class QRhiImplementation;
+ friend class QRhiProfilerPrivate;
+};
+
+Q_DECLARE_TYPEINFO(QRhiProfiler::CpuTime, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QRhiProfiler::GpuTime, Q_MOVABLE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhiprofiler_p_p.h b/src/gui/rhi/qrhiprofiler_p_p.h
new file mode 100644
index 0000000000..49c6bd78ed
--- /dev/null
+++ b/src/gui/rhi/qrhiprofiler_p_p.h
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHIPROFILER_P_H
+#define QRHIPROFILER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qrhiprofiler_p.h"
+#include <QElapsedTimer>
+#include <QHash>
+
+QT_BEGIN_NAMESPACE
+
+class QRhiProfilerPrivate
+{
+public:
+ static QRhiProfilerPrivate *get(QRhiProfiler *p) { return p->d; }
+
+ void newBuffer(QRhiBuffer *buf, quint32 realSize, int backingGpuBufCount, int backingCpuBufCount);
+ void releaseBuffer(QRhiBuffer *buf);
+ void newBufferStagingArea(QRhiBuffer *buf, int slot, quint32 size);
+ void releaseBufferStagingArea(QRhiBuffer *buf, int slot);
+
+ void newRenderBuffer(QRhiRenderBuffer *rb, bool transientBacking, bool winSysBacking, int sampleCount);
+ void releaseRenderBuffer(QRhiRenderBuffer *rb);
+
+ void newTexture(QRhiTexture *tex, bool owns, int mipCount, int layerCount, int sampleCount);
+ void releaseTexture(QRhiTexture *tex);
+ void newTextureStagingArea(QRhiTexture *tex, int slot, quint32 size);
+ void releaseTextureStagingArea(QRhiTexture *tex, int slot);
+
+ void resizeSwapChain(QRhiSwapChain *sc, int bufferCount, int msaaBufferCount, int sampleCount);
+ void releaseSwapChain(QRhiSwapChain *sc);
+
+ void beginSwapChainFrame(QRhiSwapChain *sc);
+ void endSwapChainFrame(QRhiSwapChain *sc, int frameCount);
+ void swapChainFrameGpuTime(QRhiSwapChain *sc, float gpuTimeMs);
+
+ void newReadbackBuffer(quint64 id, QRhiResource *src, quint32 size);
+ void releaseReadbackBuffer(quint64 id);
+
+ void vmemStat(int realAllocCount, int subAllocCount, quint32 totalSize, quint32 unusedSize);
+
+ void startEntry(QRhiProfiler::StreamOp op, qint64 timestamp, QRhiResource *res);
+ void writeInt(const char *key, qint64 v);
+ void writeFloat(const char *key, float f);
+ void endEntry();
+
+ QRhiImplementation *rhiDWhenEnabled = nullptr;
+ QIODevice *outputDevice = nullptr;
+ QElapsedTimer ts;
+ QByteArray buf;
+ static const int DEFAULT_FRAME_TIMING_WRITE_INTERVAL = 120; // frames
+ int frameTimingWriteInterval = DEFAULT_FRAME_TIMING_WRITE_INTERVAL;
+ struct Sc {
+ Sc() {
+ frameToFrameSamples.reserve(DEFAULT_FRAME_TIMING_WRITE_INTERVAL);
+ beginToEndSamples.reserve(DEFAULT_FRAME_TIMING_WRITE_INTERVAL);
+ gpuFrameSamples.reserve(DEFAULT_FRAME_TIMING_WRITE_INTERVAL);
+ }
+ QElapsedTimer frameToFrameTimer;
+ bool frameToFrameRunning = false;
+ QElapsedTimer beginToEndTimer;
+ QVector<qint64> frameToFrameSamples;
+ QVector<qint64> beginToEndSamples;
+ QVector<float> gpuFrameSamples;
+ QRhiProfiler::CpuTime frameToFrameTime;
+ QRhiProfiler::CpuTime beginToEndFrameTime;
+ QRhiProfiler::GpuTime gpuFrameTime;
+ };
+ QHash<QRhiSwapChain *, Sc> swapchains;
+};
+
+Q_DECLARE_TYPEINFO(QRhiProfilerPrivate::Sc, Q_MOVABLE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp
new file mode 100644
index 0000000000..f6ecd7c00e
--- /dev/null
+++ b/src/gui/rhi/qrhivulkan.cpp
@@ -0,0 +1,6063 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qrhivulkan_p_p.h"
+#include "qrhivulkanext_p.h"
+
+#define VMA_IMPLEMENTATION
+#define VMA_STATIC_VULKAN_FUNCTIONS 0
+#define VMA_RECORDING_ENABLED 0
+#define VMA_DEDICATED_ALLOCATION 0
+#ifdef QT_DEBUG
+#define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1
+#endif
+#include "vk_mem_alloc.h"
+
+#include <qmath.h>
+#include <QVulkanFunctions>
+#include <QVulkanWindow>
+
+QT_BEGIN_NAMESPACE
+
+/*
+ Vulkan 1.0 backend. Provides a double-buffered swapchain that throttles the
+ rendering thread to vsync. Textures and "static" buffers are device local,
+ and a separate, host visible staging buffer is used to upload data to them.
+ "Dynamic" buffers are in host visible memory and are duplicated (since there
+ can be 2 frames in flight). This is handled transparently to the application.
+*/
+
+/*!
+ \class QRhiVulkanInitParams
+ \inmodule QtRhi
+ \brief Vulkan specific initialization parameters.
+
+ A Vulkan-based QRhi needs at minimum a valid QVulkanInstance. It is up to
+ the user to ensure this is available and initialized. This is typically
+ done in main() similarly to the following:
+
+ \badcode
+ int main(int argc, char **argv)
+ {
+ ...
+
+ QVulkanInstance inst;
+ #ifndef Q_OS_ANDROID
+ inst.setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation");
+ #else
+ inst.setLayers(QByteArrayList()
+ << "VK_LAYER_GOOGLE_threading"
+ << "VK_LAYER_LUNARG_parameter_validation"
+ << "VK_LAYER_LUNARG_object_tracker"
+ << "VK_LAYER_LUNARG_core_validation"
+ << "VK_LAYER_LUNARG_image"
+ << "VK_LAYER_LUNARG_swapchain"
+ << "VK_LAYER_GOOGLE_unique_objects");
+ #endif
+ inst.setExtensions(QByteArrayList()
+ << "VK_KHR_get_physical_device_properties2");
+ if (!inst.create())
+ qFatal("Vulkan not available");
+
+ ...
+ }
+ \endcode
+
+ The example here has two optional aspects: it enables the
+ \l{https://github.com/KhronosGroup/Vulkan-ValidationLayers}{Vulkan
+ validation layers}, when they are available, and also enables the
+ VK_KHR_get_physical_device_properties2 extension (part of Vulkan 1.1), when
+ available. The former is useful during the development phase (remember that
+ QVulkanInstance conveniently redirects messages and warnings to qDebug).
+ Avoid enabling it in production builds, however. The latter is important in
+ order to make QRhi::CustomInstanceStepRate available with Vulkan since
+ VK_EXT_vertex_attribute_divisor (part of Vulkan 1.1) depends on it. It can
+ be omitted when instanced drawing with a non-one step rate is not used.
+
+ Once this is done, a Vulkan-based QRhi can be created by passing the
+ instance and a QWindow with its surface type set to
+ QSurface::VulkanSurface:
+
+ \badcode
+ QRhiVulkanInitParams params;
+ params.inst = vulkanInstance;
+ params.window = window;
+ rhi = QRhi::create(QRhi::Vulkan, &params);
+ \endcode
+
+ The window is optional and can be omitted. This is not recommended however
+ because there is then no way to ensure presenting is supported while
+ choosing a graphics queue.
+
+ \note Even when a window is specified, QRhiSwapChain objects can be created
+ for other windows as well, as long as they all have their
+ QWindow::surfaceType() set to QSurface::VulkanSurface.
+
+ \section2 Working with existing Vulkan devices
+
+ When interoperating with another graphics engine, it may be necessary to
+ get a QRhi instance that uses the same Vulkan device. This can be achieved
+ by passing a pointer to a QRhiVulkanNativeHandles to QRhi::create().
+
+ The physical device and device object must then be set to a non-null value.
+ In addition, either the graphics queue family index or the graphics queue
+ object itself is required. Prefer the former, whenever possible since
+ deducing the index is not possible afterwards. Optionally, an existing
+ command pool object can be specified as well, and, also optionally,
+ vmemAllocator can be used to share the same
+ \l{https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator}{Vulkan
+ memory allocator} between two QRhi instances.
+
+ The QRhi does not take ownership of any of the external objects.
+ */
+
+/*!
+ \class QRhiVulkanNativeHandles
+ \inmodule QtRhi
+ \brief Collects device, queue, and other Vulkan objects that are used by the QRhi.
+
+ \note Ownership of the Vulkan objects is never transferred.
+ */
+
+/*!
+ \class QRhiVulkanTextureNativeHandles
+ \inmodule QtRhi
+ \brief Holds the Vulkan image object that is backing a QRhiTexture.
+
+ Importing and exporting Vulkan image objects that back a QRhiTexture when
+ running with the Vulkan backend is supported via this class. Ownership of
+ the Vulkan object is never transferred.
+
+ \note Memory allocation details are not exposed. This is intentional since
+ memory is typically suballocated from a bigger chunk of VkDeviceMemory, and
+ exposing the allocator details is not desirable for now.
+ */
+
+/*!
+ \class QRhiVulkanCommandBufferNativeHandles
+ \inmodule QtRhi
+ \brief Holds the Vulkan command buffer object that is backing a QRhiCommandBuffer.
+
+ \note The Vulkan command buffer object is only guaranteed to be valid, and
+ in recording state, while recording a frame. That is, between a
+ \l{QRhi::beginFrame()}{beginFrame()} - \l{QRhi::endFrame()}{endFrame()} or
+ \l{QRhi::beginOffscreenFrame()}{beginOffscreenFrame()} -
+ \l{QRhi::endOffsrceenFrame()}{endOffscreenFrame()} pair.
+ */
+
+static inline VkDeviceSize aligned(VkDeviceSize v, VkDeviceSize byteAlign)
+{
+ return (v + byteAlign - 1) & ~(byteAlign - 1);
+}
+
+static QVulkanInstance *globalVulkanInstance;
+
+static void VKAPI_PTR wrap_vkGetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties* pProperties)
+{
+ globalVulkanInstance->functions()->vkGetPhysicalDeviceProperties(physicalDevice, pProperties);
+}
+
+static void VKAPI_PTR wrap_vkGetPhysicalDeviceMemoryProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties* pMemoryProperties)
+{
+ globalVulkanInstance->functions()->vkGetPhysicalDeviceMemoryProperties(physicalDevice, pMemoryProperties);
+}
+
+static VkResult VKAPI_PTR wrap_vkAllocateMemory(VkDevice device, const VkMemoryAllocateInfo* pAllocateInfo, const VkAllocationCallbacks* pAllocator, VkDeviceMemory* pMemory)
+{
+ return globalVulkanInstance->deviceFunctions(device)->vkAllocateMemory(device, pAllocateInfo, pAllocator, pMemory);
+}
+
+void VKAPI_PTR wrap_vkFreeMemory(VkDevice device, VkDeviceMemory memory, const VkAllocationCallbacks* pAllocator)
+{
+ globalVulkanInstance->deviceFunctions(device)->vkFreeMemory(device, memory, pAllocator);
+}
+
+VkResult VKAPI_PTR wrap_vkMapMemory(VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void** ppData)
+{
+ return globalVulkanInstance->deviceFunctions(device)->vkMapMemory(device, memory, offset, size, flags, ppData);
+}
+
+void VKAPI_PTR wrap_vkUnmapMemory(VkDevice device, VkDeviceMemory memory)
+{
+ globalVulkanInstance->deviceFunctions(device)->vkUnmapMemory(device, memory);
+}
+
+VkResult VKAPI_PTR wrap_vkFlushMappedMemoryRanges(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges)
+{
+ return globalVulkanInstance->deviceFunctions(device)->vkFlushMappedMemoryRanges(device, memoryRangeCount, pMemoryRanges);
+}
+
+VkResult VKAPI_PTR wrap_vkInvalidateMappedMemoryRanges(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges)
+{
+ return globalVulkanInstance->deviceFunctions(device)->vkInvalidateMappedMemoryRanges(device, memoryRangeCount, pMemoryRanges);
+}
+
+VkResult VKAPI_PTR wrap_vkBindBufferMemory(VkDevice device, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize memoryOffset)
+{
+ return globalVulkanInstance->deviceFunctions(device)->vkBindBufferMemory(device, buffer, memory, memoryOffset);
+}
+
+VkResult VKAPI_PTR wrap_vkBindImageMemory(VkDevice device, VkImage image, VkDeviceMemory memory, VkDeviceSize memoryOffset)
+{
+ return globalVulkanInstance->deviceFunctions(device)->vkBindImageMemory(device, image, memory, memoryOffset);
+}
+
+void VKAPI_PTR wrap_vkGetBufferMemoryRequirements(VkDevice device, VkBuffer buffer, VkMemoryRequirements* pMemoryRequirements)
+{
+ globalVulkanInstance->deviceFunctions(device)->vkGetBufferMemoryRequirements(device, buffer, pMemoryRequirements);
+}
+
+void VKAPI_PTR wrap_vkGetImageMemoryRequirements(VkDevice device, VkImage image, VkMemoryRequirements* pMemoryRequirements)
+{
+ globalVulkanInstance->deviceFunctions(device)->vkGetImageMemoryRequirements(device, image, pMemoryRequirements);
+}
+
+VkResult VKAPI_PTR wrap_vkCreateBuffer(VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer)
+{
+ return globalVulkanInstance->deviceFunctions(device)->vkCreateBuffer(device, pCreateInfo, pAllocator, pBuffer);
+}
+
+void VKAPI_PTR wrap_vkDestroyBuffer(VkDevice device, VkBuffer buffer, const VkAllocationCallbacks* pAllocator)
+{
+ globalVulkanInstance->deviceFunctions(device)->vkDestroyBuffer(device, buffer, pAllocator);
+}
+
+VkResult VKAPI_PTR wrap_vkCreateImage(VkDevice device, const VkImageCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkImage* pImage)
+{
+ return globalVulkanInstance->deviceFunctions(device)->vkCreateImage(device, pCreateInfo, pAllocator, pImage);
+}
+
+void VKAPI_PTR wrap_vkDestroyImage(VkDevice device, VkImage image, const VkAllocationCallbacks* pAllocator)
+{
+ globalVulkanInstance->deviceFunctions(device)->vkDestroyImage(device, image, pAllocator);
+}
+
+static inline VmaAllocation toVmaAllocation(QVkAlloc a)
+{
+ return reinterpret_cast<VmaAllocation>(a);
+}
+
+static inline VmaAllocator toVmaAllocator(QVkAllocator a)
+{
+ return reinterpret_cast<VmaAllocator>(a);
+}
+
+QRhiVulkan::QRhiVulkan(QRhiVulkanInitParams *params, QRhiVulkanNativeHandles *importDevice)
+ : ofr(this)
+{
+ inst = params->inst;
+ maybeWindow = params->window; // may be null
+
+ importedDevice = importDevice != nullptr;
+ if (importedDevice) {
+ physDev = importDevice->physDev;
+ dev = importDevice->dev;
+ if (physDev && dev) {
+ gfxQueueFamilyIdx = importDevice->gfxQueueFamilyIdx;
+ gfxQueue = importDevice->gfxQueue;
+ if (importDevice->cmdPool) {
+ importedCmdPool = true;
+ cmdPool = importDevice->cmdPool;
+ }
+ if (importDevice->vmemAllocator) {
+ importedAllocator = true;
+ allocator = importDevice->vmemAllocator;
+ }
+ } else {
+ qWarning("No (physical) Vulkan device is given, cannot import");
+ importedDevice = false;
+ }
+ }
+}
+
+static bool qvk_debug_filter(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object,
+ size_t location, int32_t messageCode, const char *pLayerPrefix, const char *pMessage)
+{
+ Q_UNUSED(flags);
+ Q_UNUSED(objectType);
+ Q_UNUSED(object);
+ Q_UNUSED(location);
+ Q_UNUSED(messageCode);
+ Q_UNUSED(pLayerPrefix);
+
+ // Filter out certain misleading validation layer messages, as per
+ // VulkanMemoryAllocator documentation.
+ if (strstr(pMessage, "Mapping an image with layout")
+ && strstr(pMessage, "can result in undefined behavior if this memory is used by the device"))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+bool QRhiVulkan::create(QRhi::Flags flags)
+{
+ Q_UNUSED(flags);
+ Q_ASSERT(inst);
+
+ globalVulkanInstance = inst; // assume this will not change during the lifetime of the entire application
+
+ f = inst->functions();
+
+ QVector<VkQueueFamilyProperties> queueFamilyProps;
+ auto queryQueueFamilyProps = [this, &queueFamilyProps] {
+ uint32_t queueCount = 0;
+ f->vkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, nullptr);
+ queueFamilyProps.resize(queueCount);
+ f->vkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, queueFamilyProps.data());
+ };
+
+ if (!importedDevice) {
+ uint32_t physDevCount = 0;
+ f->vkEnumeratePhysicalDevices(inst->vkInstance(), &physDevCount, nullptr);
+ if (!physDevCount) {
+ qWarning("No physical devices");
+ return false;
+ }
+ QVarLengthArray<VkPhysicalDevice, 4> physDevs(physDevCount);
+ VkResult err = f->vkEnumeratePhysicalDevices(inst->vkInstance(), &physDevCount, physDevs.data());
+ if (err != VK_SUCCESS || !physDevCount) {
+ qWarning("Failed to enumerate physical devices: %d", err);
+ return false;
+ }
+ int physDevIndex = -1;
+ int requestedPhysDevIndex = -1;
+ if (qEnvironmentVariableIsSet("QT_VK_PHYSICAL_DEVICE_INDEX"))
+ requestedPhysDevIndex = qEnvironmentVariableIntValue("QT_VK_PHYSICAL_DEVICE_INDEX");
+ for (uint32_t i = 0; i < physDevCount; ++i) {
+ f->vkGetPhysicalDeviceProperties(physDevs[i], &physDevProperties);
+ qDebug("Physical device %d: '%s' %d.%d.%d", i,
+ physDevProperties.deviceName,
+ VK_VERSION_MAJOR(physDevProperties.driverVersion),
+ VK_VERSION_MINOR(physDevProperties.driverVersion),
+ VK_VERSION_PATCH(physDevProperties.driverVersion));
+ if (physDevIndex < 0 && (requestedPhysDevIndex < 0 || requestedPhysDevIndex == int(i))) {
+ physDevIndex = i;
+ qDebug(" using this physical device");
+ }
+ }
+ if (physDevIndex < 0) {
+ qWarning("No matching physical device");
+ return false;
+ }
+ physDev = physDevs[physDevIndex];
+
+ queryQueueFamilyProps();
+
+ gfxQueue = VK_NULL_HANDLE;
+
+ // We only support combined graphics+present queues. When it comes to
+ // compute, only combined graphics+compute queue is used, compute gets
+ // disabled otherwise.
+ gfxQueueFamilyIdx = -1;
+ int computelessGfxQueueCandidateIdx = -1;
+ for (int i = 0; i < queueFamilyProps.count(); ++i) {
+ qDebug("queue family %d: flags=0x%x count=%d", i, queueFamilyProps[i].queueFlags, queueFamilyProps[i].queueCount);
+ if (gfxQueueFamilyIdx == -1
+ && (queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
+ && (!maybeWindow || inst->supportsPresent(physDev, i, maybeWindow)))
+ {
+ if (queueFamilyProps[i].queueFlags & VK_QUEUE_COMPUTE_BIT)
+ gfxQueueFamilyIdx = i;
+ else if (computelessGfxQueueCandidateIdx == -1)
+ computelessGfxQueueCandidateIdx = i;
+ }
+ }
+ if (gfxQueueFamilyIdx == -1) {
+ if (computelessGfxQueueCandidateIdx != -1) {
+ gfxQueueFamilyIdx = computelessGfxQueueCandidateIdx;
+ } else {
+ qWarning("No graphics (or no graphics+present) queue family found");
+ return false;
+ }
+ }
+
+ VkDeviceQueueCreateInfo queueInfo[2];
+ const float prio[] = { 0 };
+ memset(queueInfo, 0, sizeof(queueInfo));
+ queueInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+ queueInfo[0].queueFamilyIndex = gfxQueueFamilyIdx;
+ queueInfo[0].queueCount = 1;
+ queueInfo[0].pQueuePriorities = prio;
+
+ QVector<const char *> devLayers;
+ if (inst->layers().contains("VK_LAYER_LUNARG_standard_validation"))
+ devLayers.append("VK_LAYER_LUNARG_standard_validation");
+
+ uint32_t devExtCount = 0;
+ f->vkEnumerateDeviceExtensionProperties(physDev, nullptr, &devExtCount, nullptr);
+ QVector<VkExtensionProperties> devExts(devExtCount);
+ f->vkEnumerateDeviceExtensionProperties(physDev, nullptr, &devExtCount, devExts.data());
+ qDebug("%d device extensions available", devExts.count());
+
+ QVector<const char *> requestedDevExts;
+ requestedDevExts.append("VK_KHR_swapchain");
+
+ debugMarkersAvailable = false;
+ vertexAttribDivisorAvailable = false;
+ for (const VkExtensionProperties &ext : devExts) {
+ if (!strcmp(ext.extensionName, VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) {
+ requestedDevExts.append(VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
+ debugMarkersAvailable = true;
+ } else if (!strcmp(ext.extensionName, VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME)) {
+ if (inst->extensions().contains(QByteArrayLiteral("VK_KHR_get_physical_device_properties2"))) {
+ requestedDevExts.append(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME);
+ vertexAttribDivisorAvailable = true;
+ }
+ }
+ }
+
+ VkDeviceCreateInfo devInfo;
+ memset(&devInfo, 0, sizeof(devInfo));
+ devInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
+ devInfo.queueCreateInfoCount = 1;
+ devInfo.pQueueCreateInfos = queueInfo;
+ devInfo.enabledLayerCount = devLayers.count();
+ devInfo.ppEnabledLayerNames = devLayers.constData();
+ devInfo.enabledExtensionCount = requestedDevExts.count();
+ devInfo.ppEnabledExtensionNames = requestedDevExts.constData();
+
+ err = f->vkCreateDevice(physDev, &devInfo, nullptr, &dev);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create device: %d", err);
+ return false;
+ }
+ }
+
+ df = inst->deviceFunctions(dev);
+
+ if (!importedCmdPool) {
+ VkCommandPoolCreateInfo poolInfo;
+ memset(&poolInfo, 0, sizeof(poolInfo));
+ poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
+ poolInfo.queueFamilyIndex = gfxQueueFamilyIdx;
+ VkResult err = df->vkCreateCommandPool(dev, &poolInfo, nullptr, &cmdPool);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create command pool: %d", err);
+ return false;
+ }
+ }
+
+ if (gfxQueueFamilyIdx != -1) {
+ if (!gfxQueue)
+ df->vkGetDeviceQueue(dev, gfxQueueFamilyIdx, 0, &gfxQueue);
+
+ if (queueFamilyProps.isEmpty())
+ queryQueueFamilyProps();
+
+ hasCompute = (queueFamilyProps[gfxQueueFamilyIdx].queueFlags & VK_QUEUE_COMPUTE_BIT) != 0;
+ timestampValidBits = queueFamilyProps[gfxQueueFamilyIdx].timestampValidBits;
+ }
+
+ f->vkGetPhysicalDeviceProperties(physDev, &physDevProperties);
+ ubufAlign = physDevProperties.limits.minUniformBufferOffsetAlignment;
+ // helps little with an optimal offset of 1 (on some drivers) when the spec
+ // elsewhere states that the minimum bufferOffset is 4...
+ texbufAlign = qMax<VkDeviceSize>(4, physDevProperties.limits.optimalBufferCopyOffsetAlignment);
+
+ if (!importedAllocator) {
+ VmaVulkanFunctions afuncs;
+ afuncs.vkGetPhysicalDeviceProperties = wrap_vkGetPhysicalDeviceProperties;
+ afuncs.vkGetPhysicalDeviceMemoryProperties = wrap_vkGetPhysicalDeviceMemoryProperties;
+ afuncs.vkAllocateMemory = wrap_vkAllocateMemory;
+ afuncs.vkFreeMemory = wrap_vkFreeMemory;
+ afuncs.vkMapMemory = wrap_vkMapMemory;
+ afuncs.vkUnmapMemory = wrap_vkUnmapMemory;
+ afuncs.vkFlushMappedMemoryRanges = wrap_vkFlushMappedMemoryRanges;
+ afuncs.vkInvalidateMappedMemoryRanges = wrap_vkInvalidateMappedMemoryRanges;
+ afuncs.vkBindBufferMemory = wrap_vkBindBufferMemory;
+ afuncs.vkBindImageMemory = wrap_vkBindImageMemory;
+ afuncs.vkGetBufferMemoryRequirements = wrap_vkGetBufferMemoryRequirements;
+ afuncs.vkGetImageMemoryRequirements = wrap_vkGetImageMemoryRequirements;
+ afuncs.vkCreateBuffer = wrap_vkCreateBuffer;
+ afuncs.vkDestroyBuffer = wrap_vkDestroyBuffer;
+ afuncs.vkCreateImage = wrap_vkCreateImage;
+ afuncs.vkDestroyImage = wrap_vkDestroyImage;
+
+ VmaAllocatorCreateInfo allocatorInfo;
+ memset(&allocatorInfo, 0, sizeof(allocatorInfo));
+ allocatorInfo.physicalDevice = physDev;
+ allocatorInfo.device = dev;
+ allocatorInfo.pVulkanFunctions = &afuncs;
+ VmaAllocator vmaallocator;
+ VkResult err = vmaCreateAllocator(&allocatorInfo, &vmaallocator);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create allocator: %d", err);
+ return false;
+ }
+ allocator = vmaallocator;
+ }
+
+ inst->installDebugOutputFilter(qvk_debug_filter);
+
+ VkDescriptorPool pool;
+ VkResult err = createDescriptorPool(&pool);
+ if (err == VK_SUCCESS)
+ descriptorPools.append(pool);
+ else
+ qWarning("Failed to create initial descriptor pool: %d", err);
+
+ VkQueryPoolCreateInfo timestampQueryPoolInfo;
+ memset(&timestampQueryPoolInfo, 0, sizeof(timestampQueryPoolInfo));
+ timestampQueryPoolInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
+ timestampQueryPoolInfo.queryType = VK_QUERY_TYPE_TIMESTAMP;
+ timestampQueryPoolInfo.queryCount = QVK_MAX_ACTIVE_TIMESTAMP_PAIRS * 2;
+ err = df->vkCreateQueryPool(dev, &timestampQueryPoolInfo, nullptr, &timestampQueryPool);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create timestamp query pool: %d", err);
+ return false;
+ }
+ timestampQueryPoolMap.resize(QVK_MAX_ACTIVE_TIMESTAMP_PAIRS); // 1 bit per pair
+ timestampQueryPoolMap.fill(false);
+
+ if (debugMarkersAvailable) {
+ vkCmdDebugMarkerBegin = reinterpret_cast<PFN_vkCmdDebugMarkerBeginEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdDebugMarkerBeginEXT"));
+ vkCmdDebugMarkerEnd = reinterpret_cast<PFN_vkCmdDebugMarkerEndEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdDebugMarkerEndEXT"));
+ vkCmdDebugMarkerInsert = reinterpret_cast<PFN_vkCmdDebugMarkerInsertEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdDebugMarkerInsertEXT"));
+ vkDebugMarkerSetObjectName = reinterpret_cast<PFN_vkDebugMarkerSetObjectNameEXT>(f->vkGetDeviceProcAddr(dev, "vkDebugMarkerSetObjectNameEXT"));
+ }
+
+ nativeHandlesStruct.physDev = physDev;
+ nativeHandlesStruct.dev = dev;
+ nativeHandlesStruct.gfxQueueFamilyIdx = gfxQueueFamilyIdx;
+ nativeHandlesStruct.gfxQueue = gfxQueue;
+ nativeHandlesStruct.cmdPool = cmdPool;
+ nativeHandlesStruct.vmemAllocator = allocator;
+
+ return true;
+}
+
+void QRhiVulkan::destroy()
+{
+ if (!df)
+ return;
+
+ df->vkDeviceWaitIdle(dev);
+
+ executeDeferredReleases(true);
+ finishActiveReadbacks(true);
+
+ if (ofr.cmdFence) {
+ df->vkDestroyFence(dev, ofr.cmdFence, nullptr);
+ ofr.cmdFence = VK_NULL_HANDLE;
+ }
+
+ if (ofr.cbWrapper.cb) {
+ df->vkFreeCommandBuffers(dev, cmdPool, 1, &ofr.cbWrapper.cb);
+ ofr.cbWrapper.cb = VK_NULL_HANDLE;
+ }
+
+ if (pipelineCache) {
+ df->vkDestroyPipelineCache(dev, pipelineCache, nullptr);
+ pipelineCache = VK_NULL_HANDLE;
+ }
+
+ for (const DescriptorPoolData &pool : descriptorPools)
+ df->vkDestroyDescriptorPool(dev, pool.pool, nullptr);
+
+ descriptorPools.clear();
+
+ if (timestampQueryPool) {
+ df->vkDestroyQueryPool(dev, timestampQueryPool, nullptr);
+ timestampQueryPool = VK_NULL_HANDLE;
+ }
+
+ if (!importedAllocator && allocator) {
+ vmaDestroyAllocator(toVmaAllocator(allocator));
+ allocator = nullptr;
+ }
+
+ if (!importedCmdPool && cmdPool) {
+ df->vkDestroyCommandPool(dev, cmdPool, nullptr);
+ cmdPool = VK_NULL_HANDLE;
+ }
+
+ if (!importedDevice && dev) {
+ df->vkDestroyDevice(dev, nullptr);
+ inst->resetDeviceFunctions(dev);
+ dev = VK_NULL_HANDLE;
+ }
+
+ f = nullptr;
+ df = nullptr;
+}
+
+VkResult QRhiVulkan::createDescriptorPool(VkDescriptorPool *pool)
+{
+ VkDescriptorPoolSize descPoolSizes[] = {
+ { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, QVK_UNIFORM_BUFFERS_PER_POOL },
+ { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, QVK_UNIFORM_BUFFERS_PER_POOL },
+ { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, QVK_COMBINED_IMAGE_SAMPLERS_PER_POOL },
+ { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, QVK_STORAGE_BUFFERS_PER_POOL },
+ { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, QVK_STORAGE_IMAGES_PER_POOL }
+ };
+ VkDescriptorPoolCreateInfo descPoolInfo;
+ memset(&descPoolInfo, 0, sizeof(descPoolInfo));
+ descPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
+ // Do not enable vkFreeDescriptorSets - sets are never freed on their own
+ // (good so no trouble with fragmentation), they just deref their pool
+ // which is then reset at some point (or not).
+ descPoolInfo.flags = 0;
+ descPoolInfo.maxSets = QVK_DESC_SETS_PER_POOL;
+ descPoolInfo.poolSizeCount = sizeof(descPoolSizes) / sizeof(descPoolSizes[0]);
+ descPoolInfo.pPoolSizes = descPoolSizes;
+ return df->vkCreateDescriptorPool(dev, &descPoolInfo, nullptr, pool);
+}
+
+bool QRhiVulkan::allocateDescriptorSet(VkDescriptorSetAllocateInfo *allocInfo, VkDescriptorSet *result, int *resultPoolIndex)
+{
+ auto tryAllocate = [this, allocInfo, result](int poolIndex) {
+ allocInfo->descriptorPool = descriptorPools[poolIndex].pool;
+ VkResult r = df->vkAllocateDescriptorSets(dev, allocInfo, result);
+ if (r == VK_SUCCESS)
+ descriptorPools[poolIndex].refCount += 1;
+ return r;
+ };
+
+ int lastPoolIdx = descriptorPools.count() - 1;
+ for (int i = lastPoolIdx; i >= 0; --i) {
+ if (descriptorPools[i].refCount == 0) {
+ df->vkResetDescriptorPool(dev, descriptorPools[i].pool, 0);
+ descriptorPools[i].allocedDescSets = 0;
+ }
+ if (descriptorPools[i].allocedDescSets + allocInfo->descriptorSetCount <= QVK_DESC_SETS_PER_POOL) {
+ VkResult err = tryAllocate(i);
+ if (err == VK_SUCCESS) {
+ descriptorPools[i].allocedDescSets += allocInfo->descriptorSetCount;
+ *resultPoolIndex = i;
+ return true;
+ }
+ }
+ }
+
+ VkDescriptorPool newPool;
+ VkResult poolErr = createDescriptorPool(&newPool);
+ if (poolErr == VK_SUCCESS) {
+ descriptorPools.append(newPool);
+ lastPoolIdx = descriptorPools.count() - 1;
+ VkResult err = tryAllocate(lastPoolIdx);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to allocate descriptor set from new pool too, giving up: %d", err);
+ return false;
+ }
+ descriptorPools[lastPoolIdx].allocedDescSets += allocInfo->descriptorSetCount;
+ *resultPoolIndex = lastPoolIdx;
+ return true;
+ } else {
+ qWarning("Failed to allocate new descriptor pool: %d", poolErr);
+ return false;
+ }
+}
+
+static inline VkFormat toVkTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags)
+{
+ const bool srgb = flags.testFlag(QRhiTexture::sRGB);
+ switch (format) {
+ case QRhiTexture::RGBA8:
+ return srgb ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM;
+ case QRhiTexture::BGRA8:
+ return srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM;
+ case QRhiTexture::R8:
+ return srgb ? VK_FORMAT_R8_SRGB : VK_FORMAT_R8_UNORM;
+ case QRhiTexture::R16:
+ return VK_FORMAT_R16_UNORM;
+ case QRhiTexture::RED_OR_ALPHA8:
+ return VK_FORMAT_R8_UNORM;
+
+ case QRhiTexture::RGBA16F:
+ return VK_FORMAT_R16G16B16A16_SFLOAT;
+ case QRhiTexture::RGBA32F:
+ return VK_FORMAT_R32G32B32A32_SFLOAT;
+
+ case QRhiTexture::D16:
+ return VK_FORMAT_D16_UNORM;
+ case QRhiTexture::D32F:
+ return VK_FORMAT_D32_SFLOAT;
+
+ case QRhiTexture::BC1:
+ return srgb ? VK_FORMAT_BC1_RGB_SRGB_BLOCK : VK_FORMAT_BC1_RGB_UNORM_BLOCK;
+ case QRhiTexture::BC2:
+ return srgb ? VK_FORMAT_BC2_SRGB_BLOCK : VK_FORMAT_BC2_UNORM_BLOCK;
+ case QRhiTexture::BC3:
+ return srgb ? VK_FORMAT_BC3_SRGB_BLOCK : VK_FORMAT_BC3_UNORM_BLOCK;
+ case QRhiTexture::BC4:
+ return VK_FORMAT_BC4_UNORM_BLOCK;
+ case QRhiTexture::BC5:
+ return VK_FORMAT_BC5_UNORM_BLOCK;
+ case QRhiTexture::BC6H:
+ return VK_FORMAT_BC6H_UFLOAT_BLOCK;
+ case QRhiTexture::BC7:
+ return srgb ? VK_FORMAT_BC7_SRGB_BLOCK : VK_FORMAT_BC7_UNORM_BLOCK;
+
+ case QRhiTexture::ETC2_RGB8:
+ return srgb ? VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK : VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;
+ case QRhiTexture::ETC2_RGB8A1:
+ return srgb ? VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK : VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK;
+ case QRhiTexture::ETC2_RGBA8:
+ return srgb ? VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK : VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK;
+
+ case QRhiTexture::ASTC_4x4:
+ return srgb ? VK_FORMAT_ASTC_4x4_SRGB_BLOCK : VK_FORMAT_ASTC_4x4_UNORM_BLOCK;
+ case QRhiTexture::ASTC_5x4:
+ return srgb ? VK_FORMAT_ASTC_5x4_SRGB_BLOCK : VK_FORMAT_ASTC_5x4_UNORM_BLOCK;
+ case QRhiTexture::ASTC_5x5:
+ return srgb ? VK_FORMAT_ASTC_5x5_SRGB_BLOCK : VK_FORMAT_ASTC_5x5_UNORM_BLOCK;
+ case QRhiTexture::ASTC_6x5:
+ return srgb ? VK_FORMAT_ASTC_6x5_SRGB_BLOCK : VK_FORMAT_ASTC_6x5_UNORM_BLOCK;
+ case QRhiTexture::ASTC_6x6:
+ return srgb ? VK_FORMAT_ASTC_6x6_SRGB_BLOCK : VK_FORMAT_ASTC_6x6_UNORM_BLOCK;
+ case QRhiTexture::ASTC_8x5:
+ return srgb ? VK_FORMAT_ASTC_8x5_SRGB_BLOCK : VK_FORMAT_ASTC_8x5_UNORM_BLOCK;
+ case QRhiTexture::ASTC_8x6:
+ return srgb ? VK_FORMAT_ASTC_8x6_SRGB_BLOCK : VK_FORMAT_ASTC_8x6_UNORM_BLOCK;
+ case QRhiTexture::ASTC_8x8:
+ return srgb ? VK_FORMAT_ASTC_8x8_SRGB_BLOCK : VK_FORMAT_ASTC_8x8_UNORM_BLOCK;
+ case QRhiTexture::ASTC_10x5:
+ return srgb ? VK_FORMAT_ASTC_10x5_SRGB_BLOCK : VK_FORMAT_ASTC_10x5_UNORM_BLOCK;
+ case QRhiTexture::ASTC_10x6:
+ return srgb ? VK_FORMAT_ASTC_10x6_SRGB_BLOCK : VK_FORMAT_ASTC_10x6_UNORM_BLOCK;
+ case QRhiTexture::ASTC_10x8:
+ return srgb ? VK_FORMAT_ASTC_10x8_SRGB_BLOCK : VK_FORMAT_ASTC_10x8_UNORM_BLOCK;
+ case QRhiTexture::ASTC_10x10:
+ return srgb ? VK_FORMAT_ASTC_10x10_SRGB_BLOCK : VK_FORMAT_ASTC_10x10_UNORM_BLOCK;
+ case QRhiTexture::ASTC_12x10:
+ return srgb ? VK_FORMAT_ASTC_12x10_SRGB_BLOCK : VK_FORMAT_ASTC_12x10_UNORM_BLOCK;
+ case QRhiTexture::ASTC_12x12:
+ return srgb ? VK_FORMAT_ASTC_12x12_SRGB_BLOCK : VK_FORMAT_ASTC_12x12_UNORM_BLOCK;
+
+ default:
+ Q_UNREACHABLE();
+ return VK_FORMAT_R8G8B8A8_UNORM;
+ }
+}
+
+static inline QRhiTexture::Format colorTextureFormatFromVkFormat(VkFormat format, QRhiTexture::Flags *flags)
+{
+ switch (format) {
+ case VK_FORMAT_R8G8B8A8_UNORM:
+ return QRhiTexture::RGBA8;
+ case VK_FORMAT_R8G8B8A8_SRGB:
+ if (flags)
+ (*flags) |= QRhiTexture::sRGB;
+ return QRhiTexture::RGBA8;
+ case VK_FORMAT_B8G8R8A8_UNORM:
+ return QRhiTexture::BGRA8;
+ case VK_FORMAT_B8G8R8A8_SRGB:
+ if (flags)
+ (*flags) |= QRhiTexture::sRGB;
+ return QRhiTexture::BGRA8;
+ case VK_FORMAT_R8_UNORM:
+ return QRhiTexture::R8;
+ case VK_FORMAT_R8_SRGB:
+ if (flags)
+ (*flags) |= QRhiTexture::sRGB;
+ return QRhiTexture::R8;
+ case VK_FORMAT_R16_UNORM:
+ return QRhiTexture::R16;
+ default: // this cannot assert, must warn and return unknown
+ qWarning("VkFormat %d is not a recognized uncompressed color format", format);
+ break;
+ }
+ return QRhiTexture::UnknownFormat;
+}
+
+static inline bool isDepthTextureFormat(QRhiTexture::Format format)
+{
+ switch (format) {
+ case QRhiTexture::Format::D16:
+ Q_FALLTHROUGH();
+ case QRhiTexture::Format::D32F:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+// Transient images ("render buffers") backed by lazily allocated memory are
+// managed manually without going through vk_mem_alloc since it does not offer
+// any support for such images. This should be ok since in practice there
+// should be very few of such images.
+
+uint32_t QRhiVulkan::chooseTransientImageMemType(VkImage img, uint32_t startIndex)
+{
+ VkPhysicalDeviceMemoryProperties physDevMemProps;
+ f->vkGetPhysicalDeviceMemoryProperties(physDev, &physDevMemProps);
+
+ VkMemoryRequirements memReq;
+ df->vkGetImageMemoryRequirements(dev, img, &memReq);
+ uint32_t memTypeIndex = uint32_t(-1);
+
+ if (memReq.memoryTypeBits) {
+ // Find a device local + lazily allocated, or at least device local memtype.
+ const VkMemoryType *memType = physDevMemProps.memoryTypes;
+ bool foundDevLocal = false;
+ for (uint32_t i = startIndex; i < physDevMemProps.memoryTypeCount; ++i) {
+ if (memReq.memoryTypeBits & (1 << i)) {
+ if (memType[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
+ if (!foundDevLocal) {
+ foundDevLocal = true;
+ memTypeIndex = i;
+ }
+ if (memType[i].propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) {
+ memTypeIndex = i;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return memTypeIndex;
+}
+
+bool QRhiVulkan::createTransientImage(VkFormat format,
+ const QSize &pixelSize,
+ VkImageUsageFlags usage,
+ VkImageAspectFlags aspectMask,
+ VkSampleCountFlagBits samples,
+ VkDeviceMemory *mem,
+ VkImage *images,
+ VkImageView *views,
+ int count)
+{
+ VkMemoryRequirements memReq;
+ VkResult err;
+
+ for (int i = 0; i < count; ++i) {
+ VkImageCreateInfo imgInfo;
+ memset(&imgInfo, 0, sizeof(imgInfo));
+ imgInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+ imgInfo.imageType = VK_IMAGE_TYPE_2D;
+ imgInfo.format = format;
+ imgInfo.extent.width = pixelSize.width();
+ imgInfo.extent.height = pixelSize.height();
+ imgInfo.extent.depth = 1;
+ imgInfo.mipLevels = imgInfo.arrayLayers = 1;
+ imgInfo.samples = samples;
+ imgInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
+ imgInfo.usage = usage | VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
+ imgInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+
+ err = df->vkCreateImage(dev, &imgInfo, nullptr, images + i);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create image: %d", err);
+ return false;
+ }
+
+ // Assume the reqs are the same since the images are same in every way.
+ // Still, call GetImageMemReq for every image, in order to prevent the
+ // validation layer from complaining.
+ df->vkGetImageMemoryRequirements(dev, images[i], &memReq);
+ }
+
+ VkMemoryAllocateInfo memInfo;
+ memset(&memInfo, 0, sizeof(memInfo));
+ memInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ memInfo.allocationSize = aligned(memReq.size, memReq.alignment) * count;
+
+ uint32_t startIndex = 0;
+ do {
+ memInfo.memoryTypeIndex = chooseTransientImageMemType(images[0], startIndex);
+ if (memInfo.memoryTypeIndex == uint32_t(-1)) {
+ qWarning("No suitable memory type found");
+ return false;
+ }
+ startIndex = memInfo.memoryTypeIndex + 1;
+ err = df->vkAllocateMemory(dev, &memInfo, nullptr, mem);
+ if (err != VK_SUCCESS && err != VK_ERROR_OUT_OF_DEVICE_MEMORY) {
+ qWarning("Failed to allocate image memory: %d", err);
+ return false;
+ }
+ } while (err != VK_SUCCESS);
+
+ VkDeviceSize ofs = 0;
+ for (int i = 0; i < count; ++i) {
+ err = df->vkBindImageMemory(dev, images[i], *mem, ofs);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to bind image memory: %d", err);
+ return false;
+ }
+ ofs += aligned(memReq.size, memReq.alignment);
+
+ VkImageViewCreateInfo imgViewInfo;
+ memset(&imgViewInfo, 0, sizeof(imgViewInfo));
+ imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ imgViewInfo.image = images[i];
+ imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ imgViewInfo.format = format;
+ imgViewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
+ imgViewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
+ imgViewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
+ imgViewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
+ imgViewInfo.subresourceRange.aspectMask = aspectMask;
+ imgViewInfo.subresourceRange.levelCount = imgViewInfo.subresourceRange.layerCount = 1;
+
+ err = df->vkCreateImageView(dev, &imgViewInfo, nullptr, views + i);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create image view: %d", err);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+VkFormat QRhiVulkan::optimalDepthStencilFormat()
+{
+ if (optimalDsFormat != VK_FORMAT_UNDEFINED)
+ return optimalDsFormat;
+
+ const VkFormat dsFormatCandidates[] = {
+ VK_FORMAT_D24_UNORM_S8_UINT,
+ VK_FORMAT_D32_SFLOAT_S8_UINT,
+ VK_FORMAT_D16_UNORM_S8_UINT
+ };
+ const int dsFormatCandidateCount = sizeof(dsFormatCandidates) / sizeof(VkFormat);
+ int dsFormatIdx = 0;
+ while (dsFormatIdx < dsFormatCandidateCount) {
+ optimalDsFormat = dsFormatCandidates[dsFormatIdx];
+ VkFormatProperties fmtProp;
+ f->vkGetPhysicalDeviceFormatProperties(physDev, optimalDsFormat, &fmtProp);
+ if (fmtProp.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)
+ break;
+ ++dsFormatIdx;
+ }
+ if (dsFormatIdx == dsFormatCandidateCount)
+ qWarning("Failed to find an optimal depth-stencil format");
+
+ return optimalDsFormat;
+}
+
+bool QRhiVulkan::createDefaultRenderPass(VkRenderPass *rp, bool hasDepthStencil, VkSampleCountFlagBits samples, VkFormat colorFormat)
+{
+ VkAttachmentDescription attDesc[3];
+ memset(attDesc, 0, sizeof(attDesc));
+
+ // attachment list layout is color (1), ds (0-1), resolve (0-1)
+
+ attDesc[0].format = colorFormat;
+ attDesc[0].samples = samples;
+ attDesc[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+ attDesc[0].storeOp = samples > VK_SAMPLE_COUNT_1_BIT ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE;
+ attDesc[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ attDesc[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ attDesc[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ attDesc[0].finalLayout = samples > VK_SAMPLE_COUNT_1_BIT ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+
+ // clear on load + no store + lazy alloc + transient image should play
+ // nicely with tiled GPUs (no physical backing necessary for ds buffer)
+ attDesc[1].format = optimalDepthStencilFormat();
+ attDesc[1].samples = samples;
+ attDesc[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+ attDesc[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ attDesc[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+ attDesc[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ attDesc[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ attDesc[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+
+ if (samples > VK_SAMPLE_COUNT_1_BIT) {
+ attDesc[2].format = colorFormat;
+ attDesc[2].samples = VK_SAMPLE_COUNT_1_BIT;
+ attDesc[2].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+ attDesc[2].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+ attDesc[2].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ attDesc[2].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ attDesc[2].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ attDesc[2].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+ }
+
+ VkAttachmentReference colorRef = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
+ VkAttachmentReference dsRef = { 1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
+ VkAttachmentReference resolveRef = { 2, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
+
+ VkSubpassDescription subpassDesc;
+ memset(&subpassDesc, 0, sizeof(subpassDesc));
+ subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+ subpassDesc.colorAttachmentCount = 1;
+ subpassDesc.pColorAttachments = &colorRef;
+ subpassDesc.pDepthStencilAttachment = hasDepthStencil ? &dsRef : nullptr;
+
+ // Replace the first implicit dep (TOP_OF_PIPE / ALL_COMMANDS) with our own.
+ VkSubpassDependency subpassDep;
+ memset(&subpassDep, 0, sizeof(subpassDep));
+ subpassDep.srcSubpass = VK_SUBPASS_EXTERNAL;
+ subpassDep.dstSubpass = 0;
+ subpassDep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ subpassDep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ subpassDep.srcAccessMask = 0;
+ subpassDep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+
+ VkRenderPassCreateInfo rpInfo;
+ memset(&rpInfo, 0, sizeof(rpInfo));
+ rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+ rpInfo.attachmentCount = 1;
+ rpInfo.pAttachments = attDesc;
+ rpInfo.subpassCount = 1;
+ rpInfo.pSubpasses = &subpassDesc;
+ rpInfo.dependencyCount = 1;
+ rpInfo.pDependencies = &subpassDep;
+
+ if (hasDepthStencil)
+ rpInfo.attachmentCount += 1;
+
+ if (samples > VK_SAMPLE_COUNT_1_BIT) {
+ rpInfo.attachmentCount += 1;
+ subpassDesc.pResolveAttachments = &resolveRef;
+ }
+
+ VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, rp);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create renderpass: %d", err);
+ return false;
+ }
+
+ return true;
+}
+
+bool QRhiVulkan::createOffscreenRenderPass(VkRenderPass *rp,
+ const QVector<QRhiColorAttachment> &colorAttachments,
+ bool preserveColor,
+ bool preserveDs,
+ QRhiRenderBuffer *depthStencilBuffer,
+ QRhiTexture *depthTexture)
+{
+ QVarLengthArray<VkAttachmentDescription, 8> attDescs;
+ QVarLengthArray<VkAttachmentReference, 8> colorRefs;
+ QVarLengthArray<VkAttachmentReference, 8> resolveRefs;
+ const int colorAttCount = colorAttachments.count();
+
+ // attachment list layout is color (0-8), ds (0-1), resolve (0-8)
+
+ for (int i = 0; i < colorAttCount; ++i) {
+ QVkTexture *texD = QRHI_RES(QVkTexture, colorAttachments[i].texture());
+ QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, colorAttachments[i].renderBuffer());
+ Q_ASSERT(texD || rbD);
+ const VkFormat vkformat = texD ? texD->vkformat : rbD->vkformat;
+ const VkSampleCountFlagBits samples = texD ? texD->samples : rbD->samples;
+
+ VkAttachmentDescription attDesc;
+ memset(&attDesc, 0, sizeof(attDesc));
+ attDesc.format = vkformat;
+ attDesc.samples = samples;
+ attDesc.loadOp = preserveColor ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_CLEAR;
+ attDesc.storeOp = colorAttachments[i].resolveTexture() ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE;
+ attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ // this has to interact correctly with activateTextureRenderTarget(), hence leaving in COLOR_ATT
+ attDesc.initialLayout = preserveColor ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED;
+ attDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ attDescs.append(attDesc);
+
+ const VkAttachmentReference ref = { uint32_t(attDescs.count() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
+ colorRefs.append(ref);
+ }
+
+ const bool hasDepthStencil = depthStencilBuffer || depthTexture;
+ if (hasDepthStencil) {
+ const VkFormat dsFormat = depthTexture ? QRHI_RES(QVkTexture, depthTexture)->vkformat
+ : QRHI_RES(QVkRenderBuffer, depthStencilBuffer)->vkformat;
+ const VkSampleCountFlagBits samples = depthTexture ? QRHI_RES(QVkTexture, depthTexture)->samples
+ : QRHI_RES(QVkRenderBuffer, depthStencilBuffer)->samples;
+ const VkAttachmentLoadOp loadOp = preserveDs ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_CLEAR;
+ const VkAttachmentStoreOp storeOp = depthTexture ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ VkAttachmentDescription attDesc;
+ memset(&attDesc, 0, sizeof(attDesc));
+ attDesc.format = dsFormat;
+ attDesc.samples = samples;
+ attDesc.loadOp = loadOp;
+ attDesc.storeOp = storeOp;
+ attDesc.stencilLoadOp = loadOp;
+ attDesc.stencilStoreOp = storeOp;
+ attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ attDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+ attDescs.append(attDesc);
+ }
+ VkAttachmentReference dsRef = { uint32_t(attDescs.count() - 1), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
+
+ for (int i = 0; i < colorAttCount; ++i) {
+ if (colorAttachments[i].resolveTexture()) {
+ QVkTexture *rtexD = QRHI_RES(QVkTexture, colorAttachments[i].resolveTexture());
+ if (rtexD->samples > VK_SAMPLE_COUNT_1_BIT)
+ qWarning("Resolving into a multisample texture is not supported");
+
+ VkAttachmentDescription attDesc;
+ memset(&attDesc, 0, sizeof(attDesc));
+ attDesc.format = rtexD->vkformat;
+ attDesc.samples = VK_SAMPLE_COUNT_1_BIT;
+ attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; // ignored
+ attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+ attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ attDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ attDescs.append(attDesc);
+
+ const VkAttachmentReference ref = { uint32_t(attDescs.count() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
+ resolveRefs.append(ref);
+ } else {
+ const VkAttachmentReference ref = { VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
+ resolveRefs.append(ref);
+ }
+ }
+
+ VkSubpassDescription subpassDesc;
+ memset(&subpassDesc, 0, sizeof(subpassDesc));
+ subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+ subpassDesc.colorAttachmentCount = colorRefs.count();
+ Q_ASSERT(colorRefs.count() == resolveRefs.count());
+ subpassDesc.pColorAttachments = !colorRefs.isEmpty() ? colorRefs.constData() : nullptr;
+ subpassDesc.pDepthStencilAttachment = hasDepthStencil ? &dsRef : nullptr;
+ subpassDesc.pResolveAttachments = !resolveRefs.isEmpty() ? resolveRefs.constData() : nullptr;
+
+ VkRenderPassCreateInfo rpInfo;
+ memset(&rpInfo, 0, sizeof(rpInfo));
+ rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+ rpInfo.attachmentCount = attDescs.count();
+ rpInfo.pAttachments = attDescs.constData();
+ rpInfo.subpassCount = 1;
+ rpInfo.pSubpasses = &subpassDesc;
+ // don't yet know the correct initial/final access and stage stuff for the
+ // implicit deps at this point, so leave it to the resource tracking to
+ // generate barriers
+
+ VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, rp);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create renderpass: %d", err);
+ return false;
+ }
+
+ return true;
+}
+
+bool QRhiVulkan::recreateSwapChain(QRhiSwapChain *swapChain)
+{
+ QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
+ if (swapChainD->pixelSize.isEmpty()) {
+ qWarning("Surface size is 0, cannot create swapchain");
+ return false;
+ }
+
+ df->vkDeviceWaitIdle(dev);
+
+ if (!vkCreateSwapchainKHR) {
+ vkCreateSwapchainKHR = reinterpret_cast<PFN_vkCreateSwapchainKHR>(f->vkGetDeviceProcAddr(dev, "vkCreateSwapchainKHR"));
+ vkDestroySwapchainKHR = reinterpret_cast<PFN_vkDestroySwapchainKHR>(f->vkGetDeviceProcAddr(dev, "vkDestroySwapchainKHR"));
+ vkGetSwapchainImagesKHR = reinterpret_cast<PFN_vkGetSwapchainImagesKHR>(f->vkGetDeviceProcAddr(dev, "vkGetSwapchainImagesKHR"));
+ vkAcquireNextImageKHR = reinterpret_cast<PFN_vkAcquireNextImageKHR>(f->vkGetDeviceProcAddr(dev, "vkAcquireNextImageKHR"));
+ vkQueuePresentKHR = reinterpret_cast<PFN_vkQueuePresentKHR>(f->vkGetDeviceProcAddr(dev, "vkQueuePresentKHR"));
+ if (!vkCreateSwapchainKHR || !vkDestroySwapchainKHR || !vkGetSwapchainImagesKHR || !vkAcquireNextImageKHR || !vkQueuePresentKHR) {
+ qWarning("Swapchain functions not available");
+ return false;
+ }
+ }
+
+ VkSurfaceCapabilitiesKHR surfaceCaps;
+ vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physDev, swapChainD->surface, &surfaceCaps);
+ quint32 reqBufferCount;
+ if (swapChainD->m_flags.testFlag(QRhiSwapChain::MinimalBufferCount)) {
+ reqBufferCount = qMax<quint32>(2, surfaceCaps.minImageCount);
+ } else {
+ const quint32 maxBuffers = QVkSwapChain::MAX_BUFFER_COUNT;
+ if (surfaceCaps.maxImageCount)
+ reqBufferCount = qMax(qMin(surfaceCaps.maxImageCount, maxBuffers), surfaceCaps.minImageCount);
+ else
+ reqBufferCount = qMax<quint32>(2, surfaceCaps.minImageCount);
+ }
+
+ VkSurfaceTransformFlagBitsKHR preTransform =
+ (surfaceCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
+ ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR
+ : surfaceCaps.currentTransform;
+
+ VkCompositeAlphaFlagBitsKHR compositeAlpha =
+ (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
+ ? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR
+ : VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+
+ if (swapChainD->m_flags.testFlag(QRhiSwapChain::SurfaceHasPreMulAlpha)
+ && (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR))
+ {
+ compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
+ }
+
+ if (swapChainD->m_flags.testFlag(QRhiSwapChain::SurfaceHasNonPreMulAlpha)
+ && (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR))
+ {
+ compositeAlpha = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR;
+ }
+
+ VkImageUsageFlags usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+ swapChainD->supportsReadback = (surfaceCaps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
+ if (swapChainD->supportsReadback && swapChainD->m_flags.testFlag(QRhiSwapChain::UsedAsTransferSource))
+ usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
+
+ VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR;
+ if (swapChainD->m_flags.testFlag(QRhiSwapChain::NoVSync)) {
+ if (swapChainD->supportedPresentationModes.contains(VK_PRESENT_MODE_MAILBOX_KHR))
+ presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
+ else if (swapChainD->supportedPresentationModes.contains(VK_PRESENT_MODE_IMMEDIATE_KHR))
+ presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
+ }
+
+ // If the surface is different than before, then passing in the old
+ // swapchain associated with the old surface can fail the swapchain
+ // creation. (for example, Android loses the surface when backgrounding and
+ // restoring applications, and it also enforces failing swapchain creation
+ // with VK_ERROR_NATIVE_WINDOW_IN_USE_KHR if the old swapchain is provided)
+ const bool reuseExisting = swapChainD->sc && swapChainD->lastConnectedSurface == swapChainD->surface;
+
+ qDebug("Creating %s swapchain of %u buffers, size %dx%d, presentation mode %d",
+ reuseExisting ? "recycled" : "new",
+ reqBufferCount, swapChainD->pixelSize.width(), swapChainD->pixelSize.height(), presentMode);
+
+ VkSwapchainCreateInfoKHR swapChainInfo;
+ memset(&swapChainInfo, 0, sizeof(swapChainInfo));
+ swapChainInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
+ swapChainInfo.surface = swapChainD->surface;
+ swapChainInfo.minImageCount = reqBufferCount;
+ swapChainInfo.imageFormat = swapChainD->colorFormat;
+ swapChainInfo.imageColorSpace = swapChainD->colorSpace;
+ swapChainInfo.imageExtent = VkExtent2D { uint32_t(swapChainD->pixelSize.width()), uint32_t(swapChainD->pixelSize.height()) };
+ swapChainInfo.imageArrayLayers = 1;
+ swapChainInfo.imageUsage = usage;
+ swapChainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ swapChainInfo.preTransform = preTransform;
+ swapChainInfo.compositeAlpha = compositeAlpha;
+ swapChainInfo.presentMode = presentMode;
+ swapChainInfo.clipped = true;
+ swapChainInfo.oldSwapchain = reuseExisting ? swapChainD->sc : VK_NULL_HANDLE;
+
+ VkSwapchainKHR newSwapChain;
+ VkResult err = vkCreateSwapchainKHR(dev, &swapChainInfo, nullptr, &newSwapChain);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create swapchain: %d", err);
+ return false;
+ }
+
+ if (swapChainD->sc)
+ releaseSwapChainResources(swapChain);
+
+ swapChainD->sc = newSwapChain;
+ swapChainD->lastConnectedSurface = swapChainD->surface;
+
+ quint32 actualSwapChainBufferCount = 0;
+ err = vkGetSwapchainImagesKHR(dev, swapChainD->sc, &actualSwapChainBufferCount, nullptr);
+ if (err != VK_SUCCESS || actualSwapChainBufferCount < 2) {
+ qWarning("Failed to get swapchain images: %d (count=%u)", err, actualSwapChainBufferCount);
+ return false;
+ }
+
+ if (actualSwapChainBufferCount > QVkSwapChain::MAX_BUFFER_COUNT) {
+ qWarning("Too many swapchain buffers (%u)", actualSwapChainBufferCount);
+ return false;
+ }
+ if (actualSwapChainBufferCount != reqBufferCount)
+ qDebug("Actual swapchain buffer count is %u", actualSwapChainBufferCount);
+ swapChainD->bufferCount = actualSwapChainBufferCount;
+
+ VkImage swapChainImages[QVkSwapChain::MAX_BUFFER_COUNT];
+ err = vkGetSwapchainImagesKHR(dev, swapChainD->sc, &actualSwapChainBufferCount, swapChainImages);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to get swapchain images: %d", err);
+ return false;
+ }
+
+ VkImage msaaImages[QVkSwapChain::MAX_BUFFER_COUNT];
+ VkImageView msaaViews[QVkSwapChain::MAX_BUFFER_COUNT];
+ if (swapChainD->samples > VK_SAMPLE_COUNT_1_BIT) {
+ if (!createTransientImage(swapChainD->colorFormat,
+ swapChainD->pixelSize,
+ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
+ VK_IMAGE_ASPECT_COLOR_BIT,
+ swapChainD->samples,
+ &swapChainD->msaaImageMem,
+ msaaImages,
+ msaaViews,
+ swapChainD->bufferCount))
+ {
+ qWarning("Failed to create transient image for MSAA color buffer");
+ return false;
+ }
+ }
+
+ VkFenceCreateInfo fenceInfo;
+ memset(&fenceInfo, 0, sizeof(fenceInfo));
+ fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
+ fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
+
+ for (int i = 0; i < swapChainD->bufferCount; ++i) {
+ QVkSwapChain::ImageResources &image(swapChainD->imageRes[i]);
+ image.image = swapChainImages[i];
+ if (swapChainD->samples > VK_SAMPLE_COUNT_1_BIT) {
+ image.msaaImage = msaaImages[i];
+ image.msaaImageView = msaaViews[i];
+ }
+
+ VkImageViewCreateInfo imgViewInfo;
+ memset(&imgViewInfo, 0, sizeof(imgViewInfo));
+ imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ imgViewInfo.image = swapChainImages[i];
+ imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ imgViewInfo.format = swapChainD->colorFormat;
+ imgViewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
+ imgViewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
+ imgViewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
+ imgViewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
+ imgViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ imgViewInfo.subresourceRange.levelCount = imgViewInfo.subresourceRange.layerCount = 1;
+ err = df->vkCreateImageView(dev, &imgViewInfo, nullptr, &image.imageView);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create swapchain image view %d: %d", i, err);
+ return false;
+ }
+
+ image.lastUse = QVkSwapChain::ImageResources::ScImageUseNone;
+ }
+
+ swapChainD->currentImageIndex = 0;
+
+ VkSemaphoreCreateInfo semInfo;
+ memset(&semInfo, 0, sizeof(semInfo));
+ semInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
+ QVkSwapChain::FrameResources &frame(swapChainD->frameRes[i]);
+
+ frame.imageAcquired = false;
+ frame.imageSemWaitable = false;
+
+ df->vkCreateFence(dev, &fenceInfo, nullptr, &frame.imageFence);
+ frame.imageFenceWaitable = true; // fence was created in signaled state
+
+ df->vkCreateSemaphore(dev, &semInfo, nullptr, &frame.imageSem);
+ df->vkCreateSemaphore(dev, &semInfo, nullptr, &frame.drawSem);
+
+ err = df->vkCreateFence(dev, &fenceInfo, nullptr, &frame.cmdFence);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create command buffer fence: %d", err);
+ return false;
+ }
+ frame.cmdFenceWaitable = true; // fence was created in signaled state
+ }
+
+ swapChainD->currentFrameSlot = 0;
+
+ return true;
+}
+
+void QRhiVulkan::releaseSwapChainResources(QRhiSwapChain *swapChain)
+{
+ QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
+
+ if (swapChainD->sc == VK_NULL_HANDLE)
+ return;
+
+ df->vkDeviceWaitIdle(dev);
+
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
+ QVkSwapChain::FrameResources &frame(swapChainD->frameRes[i]);
+ if (frame.cmdFence) {
+ if (frame.cmdFenceWaitable)
+ df->vkWaitForFences(dev, 1, &frame.cmdFence, VK_TRUE, UINT64_MAX);
+ df->vkDestroyFence(dev, frame.cmdFence, nullptr);
+ frame.cmdFence = VK_NULL_HANDLE;
+ frame.cmdFenceWaitable = false;
+ }
+ if (frame.imageFence) {
+ if (frame.imageFenceWaitable)
+ df->vkWaitForFences(dev, 1, &frame.imageFence, VK_TRUE, UINT64_MAX);
+ df->vkDestroyFence(dev, frame.imageFence, nullptr);
+ frame.imageFence = VK_NULL_HANDLE;
+ frame.imageFenceWaitable = false;
+ }
+ if (frame.imageSem) {
+ df->vkDestroySemaphore(dev, frame.imageSem, nullptr);
+ frame.imageSem = VK_NULL_HANDLE;
+ }
+ if (frame.drawSem) {
+ df->vkDestroySemaphore(dev, frame.drawSem, nullptr);
+ frame.drawSem = VK_NULL_HANDLE;
+ }
+ if (frame.cmdBuf) {
+ df->vkFreeCommandBuffers(dev, cmdPool, 1, &frame.cmdBuf);
+ frame.cmdBuf = VK_NULL_HANDLE;
+ }
+ }
+
+ for (int i = 0; i < swapChainD->bufferCount; ++i) {
+ QVkSwapChain::ImageResources &image(swapChainD->imageRes[i]);
+ if (image.fb) {
+ df->vkDestroyFramebuffer(dev, image.fb, nullptr);
+ image.fb = VK_NULL_HANDLE;
+ }
+ if (image.imageView) {
+ df->vkDestroyImageView(dev, image.imageView, nullptr);
+ image.imageView = VK_NULL_HANDLE;
+ }
+ if (image.msaaImageView) {
+ df->vkDestroyImageView(dev, image.msaaImageView, nullptr);
+ image.msaaImageView = VK_NULL_HANDLE;
+ }
+ if (image.msaaImage) {
+ df->vkDestroyImage(dev, image.msaaImage, nullptr);
+ image.msaaImage = VK_NULL_HANDLE;
+ }
+ }
+
+ if (swapChainD->msaaImageMem) {
+ df->vkFreeMemory(dev, swapChainD->msaaImageMem, nullptr);
+ swapChainD->msaaImageMem = VK_NULL_HANDLE;
+ }
+
+ vkDestroySwapchainKHR(dev, swapChainD->sc, nullptr);
+ swapChainD->sc = VK_NULL_HANDLE;
+
+ // NB! surface and similar must remain intact
+}
+
+static inline bool checkDeviceLost(VkResult err)
+{
+ if (err == VK_ERROR_DEVICE_LOST) {
+ qWarning("Device lost");
+ return true;
+ }
+ return false;
+}
+
+QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags)
+{
+ Q_UNUSED(flags);
+ QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
+ QVkSwapChain::FrameResources &frame(swapChainD->frameRes[swapChainD->currentFrameSlot]);
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+
+ if (!frame.imageAcquired) {
+ // Wait if we are too far ahead, i.e. the thread gets throttled based on the presentation rate
+ // (note that we are using FIFO mode -> vsync)
+ if (frame.imageFenceWaitable) {
+ df->vkWaitForFences(dev, 1, &frame.imageFence, VK_TRUE, UINT64_MAX);
+ df->vkResetFences(dev, 1, &frame.imageFence);
+ frame.imageFenceWaitable = false;
+ }
+
+ // move on to next swapchain image
+ VkResult err = vkAcquireNextImageKHR(dev, swapChainD->sc, UINT64_MAX,
+ frame.imageSem, frame.imageFence, &frame.imageIndex);
+ if (err == VK_SUCCESS || err == VK_SUBOPTIMAL_KHR) {
+ swapChainD->currentImageIndex = frame.imageIndex;
+ frame.imageSemWaitable = true;
+ frame.imageAcquired = true;
+ frame.imageFenceWaitable = true;
+ } else if (err == VK_ERROR_OUT_OF_DATE_KHR) {
+ return QRhi::FrameOpSwapChainOutOfDate;
+ } else {
+ if (checkDeviceLost(err))
+ return QRhi::FrameOpDeviceLost;
+ else
+ qWarning("Failed to acquire next swapchain image: %d", err);
+ return QRhi::FrameOpError;
+ }
+ }
+
+ // Make sure the previous commands for the same image have finished. (note
+ // that this is based on the fence from the command buffer submit, nothing
+ // to do with the Present)
+ //
+ // Do this also for any other swapchain's commands with the same frame slot
+ // While this reduces concurrency, it keeps resource usage safe: swapchain
+ // A starting its frame 0, followed by swapchain B starting its own frame 0
+ // will make B wait for A's frame 0 commands, so if a resource is written
+ // in B's frame or when B checks for pending resource releases, that won't
+ // mess up A's in-flight commands (as they are not in flight anymore).
+ waitCommandCompletion(swapChainD->currentFrameSlot);
+
+ // Now is the time to read the timestamps for the previous frame for this slot.
+ if (frame.timestampQueryIndex >= 0) {
+ quint64 timestamp[2] = { 0, 0 };
+ VkResult err = df->vkGetQueryPoolResults(dev, timestampQueryPool, frame.timestampQueryIndex, 2,
+ 2 * sizeof(quint64), timestamp, sizeof(quint64), VK_QUERY_RESULT_64_BIT);
+ timestampQueryPoolMap.clearBit(frame.timestampQueryIndex / 2);
+ frame.timestampQueryIndex = -1;
+ if (err == VK_SUCCESS) {
+ quint64 mask = 0;
+ for (quint64 i = 0; i < timestampValidBits; i += 8)
+ mask |= 0xFFULL << i;
+ const quint64 ts0 = timestamp[0] & mask;
+ const quint64 ts1 = timestamp[1] & mask;
+ const float nsecsPerTick = physDevProperties.limits.timestampPeriod;
+ if (!qFuzzyIsNull(nsecsPerTick)) {
+ const float elapsedMs = float(ts1 - ts0) * nsecsPerTick / 1000000.0f;
+ // now we have the gpu time for the previous frame for this slot, report it
+ // (does not matter that it is not for this frame)
+ QRHI_PROF_F(swapChainFrameGpuTime(swapChain, elapsedMs));
+ }
+ } else {
+ qWarning("Failed to query timestamp: %d", err);
+ }
+ }
+
+ // build new draw command buffer
+ QRhi::FrameOpResult cbres = startCommandBuffer(&frame.cmdBuf);
+ if (cbres != QRhi::FrameOpSuccess)
+ return cbres;
+
+ // when profiling is enabled, pick a free query (pair) from the pool
+ int timestampQueryIdx = -1;
+ if (profilerPrivateOrNull()) {
+ for (int i = 0; i < timestampQueryPoolMap.count(); ++i) {
+ if (!timestampQueryPoolMap.testBit(i)) {
+ timestampQueryPoolMap.setBit(i);
+ timestampQueryIdx = i * 2;
+ break;
+ }
+ }
+ }
+ if (timestampQueryIdx >= 0) {
+ df->vkCmdResetQueryPool(frame.cmdBuf, timestampQueryPool, timestampQueryIdx, 2);
+ // record timestamp at the start of the command buffer
+ df->vkCmdWriteTimestamp(frame.cmdBuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
+ timestampQueryPool, timestampQueryIdx);
+ frame.timestampQueryIndex = timestampQueryIdx;
+ }
+
+ swapChainD->cbWrapper.cb = frame.cmdBuf;
+ QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]);
+ swapChainD->rtWrapper.d.fb = image.fb;
+
+ currentFrameSlot = swapChainD->currentFrameSlot;
+ currentSwapChain = swapChainD;
+ if (swapChainD->ds)
+ swapChainD->ds->lastActiveFrameSlot = currentFrameSlot;
+
+ QRHI_PROF_F(beginSwapChainFrame(swapChain));
+
+ prepareNewFrame(&swapChainD->cbWrapper);
+
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiVulkan::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags)
+{
+ QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
+ Q_ASSERT(currentSwapChain == swapChainD);
+
+ recordCommandBuffer(&swapChainD->cbWrapper);
+
+ QVkSwapChain::FrameResources &frame(swapChainD->frameRes[swapChainD->currentFrameSlot]);
+ QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]);
+
+ if (image.lastUse != QVkSwapChain::ImageResources::ScImageUseRender) {
+ VkImageMemoryBarrier presTrans;
+ memset(&presTrans, 0, sizeof(presTrans));
+ presTrans.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ presTrans.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ presTrans.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+ presTrans.image = image.image;
+ presTrans.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ presTrans.subresourceRange.levelCount = presTrans.subresourceRange.layerCount = 1;
+
+ if (image.lastUse == QVkSwapChain::ImageResources::ScImageUseNone) {
+ // was not used at all (no render pass), just transition from undefined to presentable
+ presTrans.srcAccessMask = 0;
+ presTrans.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ df->vkCmdPipelineBarrier(frame.cmdBuf,
+ VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+ 0, 0, nullptr, 0, nullptr,
+ 1, &presTrans);
+ } else if (image.lastUse == QVkSwapChain::ImageResources::ScImageUseTransferSource) {
+ // was used in a readback as transfer source, go back to presentable layout
+ presTrans.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
+ presTrans.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
+ df->vkCmdPipelineBarrier(frame.cmdBuf,
+ VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+ 0, 0, nullptr, 0, nullptr,
+ 1, &presTrans);
+ }
+ image.lastUse = QVkSwapChain::ImageResources::ScImageUseRender;
+ }
+
+ // record another timestamp, when enabled
+ if (frame.timestampQueryIndex >= 0) {
+ df->vkCmdWriteTimestamp(frame.cmdBuf, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
+ timestampQueryPool, frame.timestampQueryIndex + 1);
+ }
+
+ // stop recording and submit to the queue
+ Q_ASSERT(!frame.cmdFenceWaitable);
+ const bool needsPresent = !flags.testFlag(QRhi::SkipPresent);
+ QRhi::FrameOpResult submitres = endAndSubmitCommandBuffer(frame.cmdBuf,
+ frame.cmdFence,
+ frame.imageSemWaitable ? &frame.imageSem : nullptr,
+ needsPresent ? &frame.drawSem : nullptr);
+ if (submitres != QRhi::FrameOpSuccess)
+ return submitres;
+
+ frame.imageSemWaitable = false;
+ frame.cmdFenceWaitable = true;
+
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+ // this must be done before the Present
+ QRHI_PROF_F(endSwapChainFrame(swapChain, swapChainD->frameCount + 1));
+
+ if (needsPresent) {
+ // add the Present to the queue
+ VkPresentInfoKHR presInfo;
+ memset(&presInfo, 0, sizeof(presInfo));
+ presInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
+ presInfo.swapchainCount = 1;
+ presInfo.pSwapchains = &swapChainD->sc;
+ presInfo.pImageIndices = &swapChainD->currentImageIndex;
+ presInfo.waitSemaphoreCount = 1;
+ presInfo.pWaitSemaphores = &frame.drawSem; // gfxQueueFamilyIdx == presQueueFamilyIdx ? &frame.drawSem : &frame.presTransSem;
+
+ VkResult err = vkQueuePresentKHR(gfxQueue, &presInfo);
+ if (err != VK_SUCCESS) {
+ if (err == VK_ERROR_OUT_OF_DATE_KHR) {
+ return QRhi::FrameOpSwapChainOutOfDate;
+ } else if (err != VK_SUBOPTIMAL_KHR) {
+ if (checkDeviceLost(err))
+ return QRhi::FrameOpDeviceLost;
+ else
+ qWarning("Failed to present: %d", err);
+ return QRhi::FrameOpError;
+ }
+ }
+
+ // mark the current swapchain buffer as unused from our side
+ frame.imageAcquired = false;
+ // and move on to the next buffer
+ swapChainD->currentFrameSlot = (swapChainD->currentFrameSlot + 1) % QVK_FRAMES_IN_FLIGHT;
+ }
+
+ swapChainD->frameCount += 1;
+ currentSwapChain = nullptr;
+ return QRhi::FrameOpSuccess;
+}
+
+void QRhiVulkan::prepareNewFrame(QRhiCommandBuffer *cb)
+{
+ // Now is the time to do things for frame N-F, where N is the current one,
+ // F is QVK_FRAMES_IN_FLIGHT, because only here it is guaranteed that that
+ // frame has completed on the GPU (due to the fence wait in beginFrame). To
+ // decide if something is safe to handle now a simple "lastActiveFrameSlot
+ // == currentFrameSlot" is sufficient (remember that e.g. with F==2
+ // currentFrameSlot goes 0, 1, 0, 1, 0, ...)
+ //
+ // With multiple swapchains on the same QRhi things get more convoluted
+ // (and currentFrameSlot strictly alternating is not true anymore) but
+ // beginNonWrapperFrame() solves that by blocking as necessary so the rest
+ // here is safe regardless.
+
+ executeDeferredReleases();
+
+ QRHI_RES(QVkCommandBuffer, cb)->resetState();
+
+ finishActiveReadbacks(); // last, in case the readback-completed callback issues rhi calls
+}
+
+QRhi::FrameOpResult QRhiVulkan::startCommandBuffer(VkCommandBuffer *cb)
+{
+ if (*cb) {
+ df->vkFreeCommandBuffers(dev, cmdPool, 1, cb);
+ *cb = VK_NULL_HANDLE;
+ }
+
+ VkCommandBufferAllocateInfo cmdBufInfo;
+ memset(&cmdBufInfo, 0, sizeof(cmdBufInfo));
+ cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+ cmdBufInfo.commandPool = cmdPool;
+ cmdBufInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+ cmdBufInfo.commandBufferCount = 1;
+
+ VkResult err = df->vkAllocateCommandBuffers(dev, &cmdBufInfo, cb);
+ if (err != VK_SUCCESS) {
+ if (checkDeviceLost(err))
+ return QRhi::FrameOpDeviceLost;
+ else
+ qWarning("Failed to allocate frame command buffer: %d", err);
+ return QRhi::FrameOpError;
+ }
+
+ VkCommandBufferBeginInfo cmdBufBeginInfo;
+ memset(&cmdBufBeginInfo, 0, sizeof(cmdBufBeginInfo));
+ cmdBufBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+
+ err = df->vkBeginCommandBuffer(*cb, &cmdBufBeginInfo);
+ if (err != VK_SUCCESS) {
+ if (checkDeviceLost(err))
+ return QRhi::FrameOpDeviceLost;
+ else
+ qWarning("Failed to begin frame command buffer: %d", err);
+ return QRhi::FrameOpError;
+ }
+
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiVulkan::endAndSubmitCommandBuffer(VkCommandBuffer cb, VkFence cmdFence,
+ VkSemaphore *waitSem, VkSemaphore *signalSem)
+{
+ VkResult err = df->vkEndCommandBuffer(cb);
+ if (err != VK_SUCCESS) {
+ if (checkDeviceLost(err))
+ return QRhi::FrameOpDeviceLost;
+ else
+ qWarning("Failed to end frame command buffer: %d", err);
+ return QRhi::FrameOpError;
+ }
+
+ VkSubmitInfo submitInfo;
+ memset(&submitInfo, 0, sizeof(submitInfo));
+ submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ submitInfo.commandBufferCount = 1;
+ submitInfo.pCommandBuffers = &cb;
+ if (waitSem) {
+ submitInfo.waitSemaphoreCount = 1;
+ submitInfo.pWaitSemaphores = waitSem;
+ }
+ if (signalSem) {
+ submitInfo.signalSemaphoreCount = 1;
+ submitInfo.pSignalSemaphores = signalSem;
+ }
+ VkPipelineStageFlags psf = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ submitInfo.pWaitDstStageMask = &psf;
+
+ err = df->vkQueueSubmit(gfxQueue, 1, &submitInfo, cmdFence);
+ if (err != VK_SUCCESS) {
+ if (checkDeviceLost(err))
+ return QRhi::FrameOpDeviceLost;
+ else
+ qWarning("Failed to submit to graphics queue: %d", err);
+ return QRhi::FrameOpError;
+ }
+
+ return QRhi::FrameOpSuccess;
+}
+
+void QRhiVulkan::waitCommandCompletion(int frameSlot)
+{
+ for (QVkSwapChain *sc : qAsConst(swapchains)) {
+ QVkSwapChain::FrameResources &frame(sc->frameRes[frameSlot]);
+ if (frame.cmdFenceWaitable) {
+ df->vkWaitForFences(dev, 1, &frame.cmdFence, VK_TRUE, UINT64_MAX);
+ df->vkResetFences(dev, 1, &frame.cmdFence);
+ frame.cmdFenceWaitable = false;
+ }
+ }
+}
+
+QRhi::FrameOpResult QRhiVulkan::beginOffscreenFrame(QRhiCommandBuffer **cb)
+{
+ QRhi::FrameOpResult cbres = startCommandBuffer(&ofr.cbWrapper.cb);
+ if (cbres != QRhi::FrameOpSuccess)
+ return cbres;
+
+ // Switch to the next slot manually. Swapchains do not know about this
+ // which is good. So for example a - unusual but possible - onscreen,
+ // onscreen, offscreen, onscreen, onscreen, onscreen sequence of
+ // begin/endFrame leads to 0, 1, 0, 0, 1, 0. This works because the
+ // offscreen frame is synchronous in the sense that we wait for execution
+ // to complete in endFrame, and so no resources used in that frame are busy
+ // anymore in the next frame.
+ currentFrameSlot = (currentFrameSlot + 1) % QVK_FRAMES_IN_FLIGHT;
+ // except that this gets complicated with multiple swapchains so make sure
+ // any pending commands have finished for the frame slot we are going to use
+ if (swapchains.count() > 1)
+ waitCommandCompletion(currentFrameSlot);
+
+ prepareNewFrame(&ofr.cbWrapper);
+ ofr.active = true;
+
+ *cb = &ofr.cbWrapper;
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiVulkan::endOffscreenFrame()
+{
+ Q_ASSERT(ofr.active);
+ ofr.active = false;
+
+ recordCommandBuffer(&ofr.cbWrapper);
+
+ if (!ofr.cmdFence) {
+ VkFenceCreateInfo fenceInfo;
+ memset(&fenceInfo, 0, sizeof(fenceInfo));
+ fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
+ VkResult err = df->vkCreateFence(dev, &fenceInfo, nullptr, &ofr.cmdFence);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create command buffer fence: %d", err);
+ return QRhi::FrameOpError;
+ }
+ }
+
+ QRhi::FrameOpResult submitres = endAndSubmitCommandBuffer(ofr.cbWrapper.cb, ofr.cmdFence, nullptr, nullptr);
+ if (submitres != QRhi::FrameOpSuccess)
+ return submitres;
+
+ // wait for completion
+ df->vkWaitForFences(dev, 1, &ofr.cmdFence, VK_TRUE, UINT64_MAX);
+ df->vkResetFences(dev, 1, &ofr.cmdFence);
+
+ // Here we know that executing the host-side reads for this (or any
+ // previous) frame is safe since we waited for completion above.
+ finishActiveReadbacks(true);
+
+ return QRhi::FrameOpSuccess;
+}
+
+QRhi::FrameOpResult QRhiVulkan::finish()
+{
+ QVkSwapChain *swapChainD = nullptr;
+ if (inFrame) {
+ // There is either a swapchain or an offscreen frame on-going.
+ // End command recording and submit what we have.
+ VkCommandBuffer cb;
+ if (ofr.active) {
+ Q_ASSERT(!currentSwapChain);
+ recordCommandBuffer(&ofr.cbWrapper);
+ cb = ofr.cbWrapper.cb;
+ } else {
+ Q_ASSERT(currentSwapChain);
+ swapChainD = currentSwapChain;
+ recordCommandBuffer(&swapChainD->cbWrapper);
+ cb = swapChainD->cbWrapper.cb;
+ }
+ QRhi::FrameOpResult submitres = endAndSubmitCommandBuffer(cb, VK_NULL_HANDLE, nullptr, nullptr);
+ if (submitres != QRhi::FrameOpSuccess)
+ return submitres;
+ }
+
+ df->vkQueueWaitIdle(gfxQueue);
+
+ if (inFrame) {
+ // Allocate and begin recording on a new command buffer.
+ if (ofr.active)
+ startCommandBuffer(&ofr.cbWrapper.cb);
+ else
+ startCommandBuffer(&swapChainD->frameRes[swapChainD->currentFrameSlot].cmdBuf);
+ }
+
+ executeDeferredReleases(true);
+ finishActiveReadbacks(true);
+
+ return QRhi::FrameOpSuccess;
+}
+
+static inline QRhiPassResourceTracker::UsageState toPassTrackerUsageState(const QVkBuffer::UsageState &bufUsage)
+{
+ QRhiPassResourceTracker::UsageState u;
+ u.layout = 0; // unused with buffers
+ u.access = bufUsage.access;
+ u.stage = bufUsage.stage;
+ return u;
+}
+
+static inline QRhiPassResourceTracker::UsageState toPassTrackerUsageState(const QVkTexture::UsageState &texUsage)
+{
+ QRhiPassResourceTracker::UsageState u;
+ u.layout = texUsage.layout;
+ u.access = texUsage.access;
+ u.stage = texUsage.stage;
+ return u;
+}
+
+void QRhiVulkan::activateTextureRenderTarget(QVkCommandBuffer *cbD, QVkTextureRenderTarget *rtD)
+{
+ rtD->lastActiveFrameSlot = currentFrameSlot;
+ rtD->d.rp->lastActiveFrameSlot = currentFrameSlot;
+ QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
+ const QVector<QRhiColorAttachment> colorAttachments = rtD->m_desc.colorAttachments();
+ for (const QRhiColorAttachment &colorAttachment : colorAttachments) {
+ QVkTexture *texD = QRHI_RES(QVkTexture, colorAttachment.texture());
+ QVkTexture *resolveTexD = QRHI_RES(QVkTexture, colorAttachment.resolveTexture());
+ QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, colorAttachment.renderBuffer());
+ if (texD) {
+ trackedRegisterTexture(&passResTracker, texD,
+ QRhiPassResourceTracker::TexColorOutput,
+ QRhiPassResourceTracker::TexColorOutputStage);
+ texD->lastActiveFrameSlot = currentFrameSlot;
+ } else if (rbD) {
+ // Won't register rbD->backingTexture because it cannot be used for
+ // anything in a renderpass, its use makes only sense in
+ // combination with a resolveTexture.
+ rbD->lastActiveFrameSlot = currentFrameSlot;
+ }
+ if (resolveTexD) {
+ trackedRegisterTexture(&passResTracker, resolveTexD,
+ QRhiPassResourceTracker::TexColorOutput,
+ QRhiPassResourceTracker::TexColorOutputStage);
+ resolveTexD->lastActiveFrameSlot = currentFrameSlot;
+ }
+ }
+ if (rtD->m_desc.depthStencilBuffer())
+ QRHI_RES(QVkRenderBuffer, rtD->m_desc.depthStencilBuffer())->lastActiveFrameSlot = currentFrameSlot;
+ if (rtD->m_desc.depthTexture()) {
+ QVkTexture *depthTexD = QRHI_RES(QVkTexture, rtD->m_desc.depthTexture());
+ trackedRegisterTexture(&passResTracker, depthTexD,
+ QRhiPassResourceTracker::TexDepthOutput,
+ QRhiPassResourceTracker::TexDepthOutputStage);
+ depthTexD->lastActiveFrameSlot = currentFrameSlot;
+ }
+}
+
+void QRhiVulkan::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass);
+
+ enqueueResourceUpdates(cbD, resourceUpdates);
+}
+
+void QRhiVulkan::beginPass(QRhiCommandBuffer *cb,
+ QRhiRenderTarget *rt,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ QRhiResourceUpdateBatch *resourceUpdates)
+{
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass);
+
+ if (resourceUpdates)
+ enqueueResourceUpdates(cbD, resourceUpdates);
+
+ // Insert a TransitionPassResources into the command stream, pointing to
+ // the tracker this pass is going to use. That's how we generate the
+ // barriers later during recording the real VkCommandBuffer, right before
+ // the vkCmdBeginRenderPass.
+ enqueueTransitionPassResources(cbD);
+
+ QVkRenderTargetData *rtD = nullptr;
+ switch (rt->resourceType()) {
+ case QRhiResource::RenderTarget:
+ rtD = &QRHI_RES(QVkReferenceRenderTarget, rt)->d;
+ rtD->rp->lastActiveFrameSlot = currentFrameSlot;
+ Q_ASSERT(currentSwapChain);
+ currentSwapChain->imageRes[currentSwapChain->currentImageIndex].lastUse =
+ QVkSwapChain::ImageResources::ScImageUseRender;
+ break;
+ case QRhiResource::TextureRenderTarget:
+ {
+ QVkTextureRenderTarget *rtTex = QRHI_RES(QVkTextureRenderTarget, rt);
+ rtD = &rtTex->d;
+ activateTextureRenderTarget(cbD, rtTex);
+ }
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ cbD->recordingPass = QVkCommandBuffer::RenderPass;
+ cbD->currentTarget = rt;
+
+ // No copy operations or image layout transitions allowed after this point
+ // (up until endPass) as we are going to begin the renderpass.
+
+ VkRenderPassBeginInfo rpBeginInfo;
+ memset(&rpBeginInfo, 0, sizeof(rpBeginInfo));
+ rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
+ rpBeginInfo.renderPass = rtD->rp->rp;
+ rpBeginInfo.framebuffer = rtD->fb;
+ rpBeginInfo.renderArea.extent.width = rtD->pixelSize.width();
+ rpBeginInfo.renderArea.extent.height = rtD->pixelSize.height();
+
+ QVarLengthArray<VkClearValue, 4> cvs;
+ for (int i = 0; i < rtD->colorAttCount; ++i) {
+ VkClearValue cv;
+ cv.color = { { float(colorClearValue.redF()), float(colorClearValue.greenF()), float(colorClearValue.blueF()),
+ float(colorClearValue.alphaF()) } };
+ cvs.append(cv);
+ }
+ for (int i = 0; i < rtD->dsAttCount; ++i) {
+ VkClearValue cv;
+ cv.depthStencil = { depthStencilClearValue.depthClearValue(), depthStencilClearValue.stencilClearValue() };
+ cvs.append(cv);
+ }
+ for (int i = 0; i < rtD->resolveAttCount; ++i) {
+ VkClearValue cv;
+ cv.color = { { float(colorClearValue.redF()), float(colorClearValue.greenF()), float(colorClearValue.blueF()),
+ float(colorClearValue.alphaF()) } };
+ cvs.append(cv);
+ }
+ rpBeginInfo.clearValueCount = cvs.count();
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::BeginRenderPass;
+ cmd.args.beginRenderPass.desc = rpBeginInfo;
+ cmd.args.beginRenderPass.clearValueIndex = cbD->pools.clearValue.count();
+ cbD->pools.clearValue.append(cvs.constData(), cvs.count());
+ cbD->commands.append(cmd);
+}
+
+void QRhiVulkan::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::EndRenderPass;
+ cbD->commands.append(cmd);
+
+ cbD->recordingPass = QVkCommandBuffer::NoPass;
+ cbD->currentTarget = nullptr;
+
+ if (resourceUpdates)
+ enqueueResourceUpdates(cbD, resourceUpdates);
+}
+
+void QRhiVulkan::beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass);
+
+ if (resourceUpdates)
+ enqueueResourceUpdates(cbD, resourceUpdates);
+
+ enqueueTransitionPassResources(cbD);
+
+ cbD->recordingPass = QVkCommandBuffer::ComputePass;
+}
+
+void QRhiVulkan::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::ComputePass);
+
+ cbD->recordingPass = QVkCommandBuffer::NoPass;
+
+ if (resourceUpdates)
+ enqueueResourceUpdates(cbD, resourceUpdates);
+}
+
+void QRhiVulkan::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps)
+{
+ QVkComputePipeline *psD = QRHI_RES(QVkComputePipeline, ps);
+ Q_ASSERT(psD->pipeline);
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::ComputePass);
+
+ if (cbD->currentComputePipeline != ps || cbD->currentPipelineGeneration != psD->generation) {
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::BindPipeline;
+ cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_COMPUTE;
+ cmd.args.bindPipeline.pipeline = psD->pipeline;
+ cbD->commands.append(cmd);
+
+ cbD->currentGraphicsPipeline = nullptr;
+ cbD->currentComputePipeline = ps;
+ cbD->currentPipelineGeneration = psD->generation;
+ }
+
+ psD->lastActiveFrameSlot = currentFrameSlot;
+}
+
+void QRhiVulkan::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
+{
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::ComputePass);
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::Dispatch;
+ cmd.args.dispatch.x = x;
+ cmd.args.dispatch.y = y;
+ cmd.args.dispatch.z = z;
+ cbD->commands.append(cmd);
+}
+
+VkShaderModule QRhiVulkan::createShader(const QByteArray &spirv)
+{
+ VkShaderModuleCreateInfo shaderInfo;
+ memset(&shaderInfo, 0, sizeof(shaderInfo));
+ shaderInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
+ shaderInfo.codeSize = spirv.size();
+ shaderInfo.pCode = reinterpret_cast<const quint32 *>(spirv.constData());
+ VkShaderModule shaderModule;
+ VkResult err = df->vkCreateShaderModule(dev, &shaderInfo, nullptr, &shaderModule);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create shader module: %d", err);
+ return VK_NULL_HANDLE;
+ }
+ return shaderModule;
+}
+
+bool QRhiVulkan::ensurePipelineCache()
+{
+ if (pipelineCache)
+ return true;
+
+ VkPipelineCacheCreateInfo pipelineCacheInfo;
+ memset(&pipelineCacheInfo, 0, sizeof(pipelineCacheInfo));
+ pipelineCacheInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
+ VkResult err = df->vkCreatePipelineCache(dev, &pipelineCacheInfo, nullptr, &pipelineCache);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create pipeline cache: %d", err);
+ return false;
+ }
+ return true;
+}
+
+void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, int descSetIdx)
+{
+ QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, srb);
+
+ QVarLengthArray<VkDescriptorBufferInfo, 4> bufferInfos;
+ QVarLengthArray<VkDescriptorImageInfo, 4> imageInfos;
+ QVarLengthArray<VkWriteDescriptorSet, 8> writeInfos;
+
+ const bool updateAll = descSetIdx < 0;
+ int frameSlot = updateAll ? 0 : descSetIdx;
+ while (frameSlot < (updateAll ? QVK_FRAMES_IN_FLIGHT : descSetIdx + 1)) {
+ srbD->boundResourceData[frameSlot].resize(srbD->sortedBindings.count());
+ for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
+ const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&srbD->sortedBindings[i]);
+ QVkShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[frameSlot][i]);
+
+ VkWriteDescriptorSet writeInfo;
+ memset(&writeInfo, 0, sizeof(writeInfo));
+ writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ writeInfo.dstSet = srbD->descSets[frameSlot];
+ writeInfo.dstBinding = b->binding;
+ writeInfo.descriptorCount = 1;
+
+ switch (b->type) {
+ case QRhiShaderResourceBinding::UniformBuffer:
+ {
+ writeInfo.descriptorType = b->u.ubuf.hasDynamicOffset ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC
+ : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ QRhiBuffer *buf = b->u.ubuf.buf;
+ QVkBuffer *bufD = QRHI_RES(QVkBuffer, buf);
+ bd.ubuf.id = bufD->m_id;
+ bd.ubuf.generation = bufD->generation;
+ VkDescriptorBufferInfo bufInfo;
+ bufInfo.buffer = bufD->m_type == QRhiBuffer::Dynamic ? bufD->buffers[frameSlot] : bufD->buffers[0];
+ bufInfo.offset = b->u.ubuf.offset;
+ bufInfo.range = b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size;
+ // be nice and assert when we know the vulkan device would die a horrible death due to non-aligned reads
+ Q_ASSERT(aligned(bufInfo.offset, ubufAlign) == bufInfo.offset);
+ bufferInfos.append(bufInfo);
+ writeInfo.pBufferInfo = &bufferInfos.last();
+ }
+ break;
+ case QRhiShaderResourceBinding::SampledTexture:
+ {
+ QVkTexture *texD = QRHI_RES(QVkTexture, b->u.stex.tex);
+ QVkSampler *samplerD = QRHI_RES(QVkSampler, b->u.stex.sampler);
+ writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ bd.stex.texId = texD->m_id;
+ bd.stex.texGeneration = texD->generation;
+ bd.stex.samplerId = samplerD->m_id;
+ bd.stex.samplerGeneration = samplerD->generation;
+ VkDescriptorImageInfo imageInfo;
+ imageInfo.sampler = samplerD->sampler;
+ imageInfo.imageView = texD->imageView;
+ imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ imageInfos.append(imageInfo);
+ writeInfo.pImageInfo = &imageInfos.last();
+ }
+ break;
+ case QRhiShaderResourceBinding::ImageLoad:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::ImageStore:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::ImageLoadStore:
+ {
+ QVkTexture *texD = QRHI_RES(QVkTexture, b->u.simage.tex);
+ VkImageView view = texD->imageViewForLevel(b->u.simage.level);
+ if (view) {
+ writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
+ bd.simage.id = texD->m_id;
+ bd.simage.generation = texD->generation;
+ VkDescriptorImageInfo imageInfo;
+ imageInfo.sampler = VK_NULL_HANDLE;
+ imageInfo.imageView = view;
+ imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
+ imageInfos.append(imageInfo);
+ writeInfo.pImageInfo = &imageInfos.last();
+ }
+ }
+ break;
+ case QRhiShaderResourceBinding::BufferLoad:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::BufferStore:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::BufferLoadStore:
+ {
+ QVkBuffer *bufD = QRHI_RES(QVkBuffer, b->u.sbuf.buf);
+ writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+ bd.sbuf.id = bufD->m_id;
+ bd.sbuf.generation = bufD->generation;
+ VkDescriptorBufferInfo bufInfo;
+ bufInfo.buffer = bufD->m_type == QRhiBuffer::Dynamic ? bufD->buffers[frameSlot] : bufD->buffers[0];
+ bufInfo.offset = b->u.ubuf.offset;
+ bufInfo.range = b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size;
+ bufferInfos.append(bufInfo);
+ writeInfo.pBufferInfo = &bufferInfos.last();
+ }
+ break;
+ default:
+ continue;
+ }
+
+ writeInfos.append(writeInfo);
+ }
+ ++frameSlot;
+ }
+
+ df->vkUpdateDescriptorSets(dev, writeInfos.count(), writeInfos.constData(), 0, nullptr);
+}
+
+static inline bool accessIsWrite(VkAccessFlags access)
+{
+ return (access & VK_ACCESS_SHADER_WRITE_BIT) != 0
+ || (access & VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT) != 0
+ || (access & VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT) != 0
+ || (access & VK_ACCESS_TRANSFER_WRITE_BIT) != 0
+ || (access & VK_ACCESS_HOST_WRITE_BIT) != 0
+ || (access & VK_ACCESS_MEMORY_WRITE_BIT) != 0;
+}
+
+void QRhiVulkan::trackedBufferBarrier(QVkCommandBuffer *cbD, QVkBuffer *bufD, int slot,
+ VkAccessFlags access, VkPipelineStageFlags stage)
+{
+ Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass);
+ Q_ASSERT(access && stage);
+ QVkBuffer::UsageState &s(bufD->usageState[slot]);
+ if (!s.stage) {
+ s.access = access;
+ s.stage = stage;
+ return;
+ }
+
+ if (s.access == access && s.stage == stage) {
+ // No need to flood with unnecessary read-after-read barriers.
+ // Write-after-write is a different matter, however.
+ if (!accessIsWrite(access))
+ return;
+ }
+
+ VkBufferMemoryBarrier bufMemBarrier;
+ memset(&bufMemBarrier, 0, sizeof(bufMemBarrier));
+ bufMemBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
+ bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ bufMemBarrier.srcAccessMask = s.access;
+ bufMemBarrier.dstAccessMask = access;
+ bufMemBarrier.buffer = bufD->buffers[slot];
+ bufMemBarrier.size = VK_WHOLE_SIZE;
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::BufferBarrier;
+ cmd.args.bufferBarrier.srcStageMask = s.stage;
+ cmd.args.bufferBarrier.dstStageMask = stage;
+ cmd.args.bufferBarrier.desc = bufMemBarrier;
+ cbD->commands.append(cmd);
+
+ s.access = access;
+ s.stage = stage;
+}
+
+void QRhiVulkan::trackedImageBarrier(QVkCommandBuffer *cbD, QVkTexture *texD,
+ VkImageLayout layout, VkAccessFlags access, VkPipelineStageFlags stage)
+{
+ Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass);
+ Q_ASSERT(layout && access && stage);
+ QVkTexture::UsageState &s(texD->usageState);
+ if (s.access == access && s.stage == stage && s.layout == layout) {
+ if (!accessIsWrite(access))
+ return;
+ }
+
+ VkImageMemoryBarrier barrier;
+ memset(&barrier, 0, sizeof(barrier));
+ barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ barrier.subresourceRange.aspectMask = !isDepthTextureFormat(texD->m_format)
+ ? VK_IMAGE_ASPECT_COLOR_BIT : VK_IMAGE_ASPECT_DEPTH_BIT;
+ barrier.subresourceRange.baseMipLevel = 0;
+ barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
+ barrier.subresourceRange.baseArrayLayer = 0;
+ barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
+ barrier.oldLayout = s.layout; // new textures have this set to PREINITIALIZED
+ barrier.newLayout = layout;
+ barrier.srcAccessMask = s.access; // may be 0 but that's fine
+ barrier.dstAccessMask = access;
+ barrier.image = texD->image;
+
+ VkPipelineStageFlags srcStage = s.stage;
+ // stage mask cannot be 0
+ if (!srcStage)
+ srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::ImageBarrier;
+ cmd.args.imageBarrier.srcStageMask = srcStage;
+ cmd.args.imageBarrier.dstStageMask = stage;
+ cmd.args.imageBarrier.desc = barrier;
+ cbD->commands.append(cmd);
+
+ s.layout = layout;
+ s.access = access;
+ s.stage = stage;
+}
+
+void QRhiVulkan::subresourceBarrier(QVkCommandBuffer *cbD, VkImage image,
+ VkImageLayout oldLayout, VkImageLayout newLayout,
+ VkAccessFlags srcAccess, VkAccessFlags dstAccess,
+ VkPipelineStageFlags srcStage, VkPipelineStageFlags dstStage,
+ int startLayer, int layerCount,
+ int startLevel, int levelCount)
+{
+ Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass);
+ VkImageMemoryBarrier barrier;
+ memset(&barrier, 0, sizeof(barrier));
+ barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ barrier.subresourceRange.baseMipLevel = startLevel;
+ barrier.subresourceRange.levelCount = levelCount;
+ barrier.subresourceRange.baseArrayLayer = startLayer;
+ barrier.subresourceRange.layerCount = layerCount;
+ barrier.oldLayout = oldLayout;
+ barrier.newLayout = newLayout;
+ barrier.srcAccessMask = srcAccess;
+ barrier.dstAccessMask = dstAccess;
+ barrier.image = image;
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::ImageBarrier;
+ cmd.args.imageBarrier.srcStageMask = srcStage;
+ cmd.args.imageBarrier.dstStageMask = dstStage;
+ cmd.args.imageBarrier.desc = barrier;
+ cbD->commands.append(cmd);
+}
+
+VkDeviceSize QRhiVulkan::subresUploadByteSize(const QRhiTextureSubresourceUploadDescription &subresDesc) const
+{
+ VkDeviceSize size = 0;
+ const qsizetype imageSizeBytes = subresDesc.image().isNull() ?
+ subresDesc.data().size() : subresDesc.image().sizeInBytes();
+ if (imageSizeBytes > 0)
+ size += aligned(imageSizeBytes, texbufAlign);
+ return size;
+}
+
+void QRhiVulkan::prepareUploadSubres(QVkTexture *texD, int layer, int level,
+ const QRhiTextureSubresourceUploadDescription &subresDesc,
+ size_t *curOfs, void *mp,
+ BufferImageCopyList *copyInfos)
+{
+ qsizetype copySizeBytes = 0;
+ qsizetype imageSizeBytes = 0;
+ const void *src = nullptr;
+
+ VkBufferImageCopy copyInfo;
+ memset(&copyInfo, 0, sizeof(copyInfo));
+ copyInfo.bufferOffset = *curOfs;
+ copyInfo.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ copyInfo.imageSubresource.mipLevel = level;
+ copyInfo.imageSubresource.baseArrayLayer = layer;
+ copyInfo.imageSubresource.layerCount = 1;
+ copyInfo.imageExtent.depth = 1;
+
+ const QByteArray rawData = subresDesc.data();
+ const QPoint dp = subresDesc.destinationTopLeft();
+ QImage image = subresDesc.image();
+ if (!image.isNull()) {
+ copySizeBytes = imageSizeBytes = image.sizeInBytes();
+ QSize size = image.size();
+ src = image.constBits();
+ // Scanlines in QImage are 4 byte aligned so bpl must
+ // be taken into account for bufferRowLength.
+ int bpc = qMax(1, image.depth() / 8);
+ // this is in pixels, not bytes, to make it more complicated...
+ copyInfo.bufferRowLength = image.bytesPerLine() / bpc;
+ if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
+ const int sx = subresDesc.sourceTopLeft().x();
+ const int sy = subresDesc.sourceTopLeft().y();
+ if (!subresDesc.sourceSize().isEmpty())
+ size = subresDesc.sourceSize();
+ if (image.depth() == 32) {
+ // The staging buffer will get the full image
+ // regardless, just adjust the vk
+ // buffer-to-image copy start offset.
+ copyInfo.bufferOffset += sy * image.bytesPerLine() + sx * 4;
+ // bufferRowLength remains set to the original image's width
+ } else {
+ image = image.copy(sx, sy, size.width(), size.height());
+ src = image.constBits();
+ // The staging buffer gets the slice only. The rest of the
+ // space reserved for this mip will be unused.
+ copySizeBytes = image.sizeInBytes();
+ bpc = qMax(1, image.depth() / 8);
+ copyInfo.bufferRowLength = image.bytesPerLine() / bpc;
+ }
+ }
+ copyInfo.imageOffset.x = dp.x();
+ copyInfo.imageOffset.y = dp.y();
+ copyInfo.imageExtent.width = size.width();
+ copyInfo.imageExtent.height = size.height();
+ copyInfos->append(copyInfo);
+ } else if (!rawData.isEmpty() && isCompressedFormat(texD->m_format)) {
+ copySizeBytes = imageSizeBytes = rawData.size();
+ src = rawData.constData();
+ QSize size = q->sizeForMipLevel(level, texD->m_pixelSize);
+ const int subresw = size.width();
+ const int subresh = size.height();
+ if (!subresDesc.sourceSize().isEmpty())
+ size = subresDesc.sourceSize();
+ const int w = size.width();
+ const int h = size.height();
+ QSize blockDim;
+ compressedFormatInfo(texD->m_format, QSize(w, h), nullptr, nullptr, &blockDim);
+ // x and y must be multiples of the block width and height
+ copyInfo.imageOffset.x = aligned(dp.x(), blockDim.width());
+ copyInfo.imageOffset.y = aligned(dp.y(), blockDim.height());
+ // width and height must be multiples of the block width and height
+ // or x + width and y + height must equal the subresource width and height
+ copyInfo.imageExtent.width = dp.x() + w == subresw ? w : aligned(w, blockDim.width());
+ copyInfo.imageExtent.height = dp.y() + h == subresh ? h : aligned(h, blockDim.height());
+ copyInfos->append(copyInfo);
+ } else if (!rawData.isEmpty()) {
+ copySizeBytes = imageSizeBytes = rawData.size();
+ src = rawData.constData();
+ QSize size = q->sizeForMipLevel(level, texD->m_pixelSize);
+ if (!subresDesc.sourceSize().isEmpty())
+ size = subresDesc.sourceSize();
+ copyInfo.imageOffset.x = dp.x();
+ copyInfo.imageOffset.y = dp.y();
+ copyInfo.imageExtent.width = size.width();
+ copyInfo.imageExtent.height = size.height();
+ copyInfos->append(copyInfo);
+ } else {
+ qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
+ }
+
+ memcpy(reinterpret_cast<char *>(mp) + *curOfs, src, copySizeBytes);
+ *curOfs += aligned(imageSizeBytes, texbufAlign);
+}
+
+void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates)
+{
+ QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+
+ for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : ud->dynamicBufferUpdates) {
+ QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf);
+ Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
+ bufD->pendingDynamicUpdates[i].append(u);
+ }
+
+ for (const QRhiResourceUpdateBatchPrivate::StaticBufferUpload &u : ud->staticBufferUploads) {
+ QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf);
+ Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
+ Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
+
+ if (!bufD->stagingBuffers[currentFrameSlot]) {
+ VkBufferCreateInfo bufferInfo;
+ memset(&bufferInfo, 0, sizeof(bufferInfo));
+ bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ // must cover the entire buffer - this way multiple, partial updates per frame
+ // are supported even when the staging buffer is reused (Static)
+ bufferInfo.size = bufD->m_size;
+ bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
+
+ VmaAllocationCreateInfo allocInfo;
+ memset(&allocInfo, 0, sizeof(allocInfo));
+ allocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
+
+ VmaAllocation allocation;
+ VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo,
+ &bufD->stagingBuffers[currentFrameSlot], &allocation, nullptr);
+ if (err == VK_SUCCESS) {
+ bufD->stagingAllocations[currentFrameSlot] = allocation;
+ QRHI_PROF_F(newBufferStagingArea(bufD, currentFrameSlot, bufD->m_size));
+ } else {
+ qWarning("Failed to create staging buffer of size %d: %d", bufD->m_size, err);
+ continue;
+ }
+ }
+
+ void *p = nullptr;
+ VmaAllocation a = toVmaAllocation(bufD->stagingAllocations[currentFrameSlot]);
+ VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to map buffer: %d", err);
+ continue;
+ }
+ memcpy(static_cast<uchar *>(p) + u.offset, u.data.constData(), u.data.size());
+ vmaUnmapMemory(toVmaAllocator(allocator), a);
+ vmaFlushAllocation(toVmaAllocator(allocator), a, u.offset, u.data.size());
+
+ trackedBufferBarrier(cbD, bufD, 0,
+ VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
+
+ VkBufferCopy copyInfo;
+ memset(&copyInfo, 0, sizeof(copyInfo));
+ copyInfo.srcOffset = u.offset;
+ copyInfo.dstOffset = u.offset;
+ copyInfo.size = u.data.size();
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::CopyBuffer;
+ cmd.args.copyBuffer.src = bufD->stagingBuffers[currentFrameSlot];
+ cmd.args.copyBuffer.dst = bufD->buffers[0];
+ cmd.args.copyBuffer.desc = copyInfo;
+ cbD->commands.append(cmd);
+
+ // Where's the barrier for read-after-write? (assuming the common case
+ // of binding this buffer as vertex/index, or, less likely, as uniform
+ // buffer, in a renderpass later on) That is handled by the pass
+ // resource tracking: the appropriate pipeline barrier will be
+ // generated and recorded right before the renderpass, that binds this
+ // buffer in one of its commands, gets its BeginRenderPass recorded.
+
+ bufD->lastActiveFrameSlot = currentFrameSlot;
+
+ if (bufD->m_type == QRhiBuffer::Immutable) {
+ QRhiVulkan::DeferredReleaseEntry e;
+ e.type = QRhiVulkan::DeferredReleaseEntry::StagingBuffer;
+ e.lastActiveFrameSlot = currentFrameSlot;
+ e.stagingBuffer.stagingBuffer = bufD->stagingBuffers[currentFrameSlot];
+ e.stagingBuffer.stagingAllocation = bufD->stagingAllocations[currentFrameSlot];
+ bufD->stagingBuffers[currentFrameSlot] = VK_NULL_HANDLE;
+ bufD->stagingAllocations[currentFrameSlot] = nullptr;
+ releaseQueue.append(e);
+ QRHI_PROF_F(releaseBufferStagingArea(bufD, currentFrameSlot));
+ }
+ }
+
+ for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) {
+ if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
+ QVkTexture *utexD = QRHI_RES(QVkTexture, u.upload.tex);
+ // batch into a single staging buffer and a single CopyBufferToImage with multiple copyInfos
+ VkDeviceSize stagingSize = 0;
+ for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
+ for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
+ for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.upload.subresDesc[layer][level]))
+ stagingSize += subresUploadByteSize(subresDesc);
+ }
+ }
+
+ Q_ASSERT(!utexD->stagingBuffers[currentFrameSlot]);
+ VkBufferCreateInfo bufferInfo;
+ memset(&bufferInfo, 0, sizeof(bufferInfo));
+ bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ bufferInfo.size = stagingSize;
+ bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
+
+ VmaAllocationCreateInfo allocInfo;
+ memset(&allocInfo, 0, sizeof(allocInfo));
+ allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
+
+ VmaAllocation allocation;
+ VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo,
+ &utexD->stagingBuffers[currentFrameSlot], &allocation, nullptr);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create image staging buffer of size %d: %d", int(stagingSize), err);
+ continue;
+ }
+ utexD->stagingAllocations[currentFrameSlot] = allocation;
+ QRHI_PROF_F(newTextureStagingArea(utexD, currentFrameSlot, stagingSize));
+
+ BufferImageCopyList copyInfos;
+ size_t curOfs = 0;
+ void *mp = nullptr;
+ VmaAllocation a = toVmaAllocation(utexD->stagingAllocations[currentFrameSlot]);
+ err = vmaMapMemory(toVmaAllocator(allocator), a, &mp);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to map image data: %d", err);
+ continue;
+ }
+
+ for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
+ for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
+ const QVector<QRhiTextureSubresourceUploadDescription> &srd(u.upload.subresDesc[layer][level]);
+ if (srd.isEmpty())
+ continue;
+ for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(srd)) {
+ prepareUploadSubres(utexD, layer, level,
+ subresDesc, &curOfs, mp, &copyInfos);
+ }
+ }
+ }
+ vmaUnmapMemory(toVmaAllocator(allocator), a);
+ vmaFlushAllocation(toVmaAllocator(allocator), a, 0, stagingSize);
+
+ trackedImageBarrier(cbD, utexD, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::CopyBufferToImage;
+ cmd.args.copyBufferToImage.src = utexD->stagingBuffers[currentFrameSlot];
+ cmd.args.copyBufferToImage.dst = utexD->image;
+ cmd.args.copyBufferToImage.dstLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+ cmd.args.copyBufferToImage.count = copyInfos.count();
+ cmd.args.copyBufferToImage.bufferImageCopyIndex = cbD->pools.bufferImageCopy.count();
+ cbD->pools.bufferImageCopy.append(copyInfos.constData(), copyInfos.count());
+ cbD->commands.append(cmd);
+
+ // no reuse of staging, this is intentional
+ QRhiVulkan::DeferredReleaseEntry e;
+ e.type = QRhiVulkan::DeferredReleaseEntry::StagingBuffer;
+ e.lastActiveFrameSlot = currentFrameSlot;
+ e.stagingBuffer.stagingBuffer = utexD->stagingBuffers[currentFrameSlot];
+ e.stagingBuffer.stagingAllocation = utexD->stagingAllocations[currentFrameSlot];
+ utexD->stagingBuffers[currentFrameSlot] = VK_NULL_HANDLE;
+ utexD->stagingAllocations[currentFrameSlot] = nullptr;
+ releaseQueue.append(e);
+ QRHI_PROF_F(releaseTextureStagingArea(utexD, currentFrameSlot));
+
+ // Similarly to buffers, transitioning away from DST is done later,
+ // when a renderpass using the texture is encountered.
+
+ utexD->lastActiveFrameSlot = currentFrameSlot;
+ } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) {
+ Q_ASSERT(u.copy.src && u.copy.dst);
+ if (u.copy.src == u.copy.dst) {
+ qWarning("Texture copy with matching source and destination is not supported");
+ continue;
+ }
+ QVkTexture *srcD = QRHI_RES(QVkTexture, u.copy.src);
+ QVkTexture *dstD = QRHI_RES(QVkTexture, u.copy.dst);
+
+ VkImageCopy region;
+ memset(&region, 0, sizeof(region));
+
+ region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ region.srcSubresource.mipLevel = u.copy.desc.sourceLevel();
+ region.srcSubresource.baseArrayLayer = u.copy.desc.sourceLayer();
+ region.srcSubresource.layerCount = 1;
+
+ region.srcOffset.x = u.copy.desc.sourceTopLeft().x();
+ region.srcOffset.y = u.copy.desc.sourceTopLeft().y();
+
+ region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ region.dstSubresource.mipLevel = u.copy.desc.destinationLevel();
+ region.dstSubresource.baseArrayLayer = u.copy.desc.destinationLayer();
+ region.dstSubresource.layerCount = 1;
+
+ region.dstOffset.x = u.copy.desc.destinationTopLeft().x();
+ region.dstOffset.y = u.copy.desc.destinationTopLeft().y();
+
+ const QSize size = u.copy.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : u.copy.desc.pixelSize();
+ region.extent.width = size.width();
+ region.extent.height = size.height();
+ region.extent.depth = 1;
+
+ trackedImageBarrier(cbD, srcD, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
+ trackedImageBarrier(cbD, dstD, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::CopyImage;
+ cmd.args.copyImage.src = srcD->image;
+ cmd.args.copyImage.srcLayout = srcD->usageState.layout;
+ cmd.args.copyImage.dst = dstD->image;
+ cmd.args.copyImage.dstLayout = dstD->usageState.layout;
+ cmd.args.copyImage.desc = region;
+ cbD->commands.append(cmd);
+
+ srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot;
+ } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
+ ActiveReadback aRb;
+ aRb.activeFrameSlot = currentFrameSlot;
+ aRb.desc = u.read.rb;
+ aRb.result = u.read.result;
+
+ QVkTexture *texD = QRHI_RES(QVkTexture, u.read.rb.texture());
+ QVkSwapChain *swapChainD = nullptr;
+ if (texD) {
+ if (texD->samples > VK_SAMPLE_COUNT_1_BIT) {
+ qWarning("Multisample texture cannot be read back");
+ continue;
+ }
+ aRb.pixelSize = u.read.rb.level() > 0 ? q->sizeForMipLevel(u.read.rb.level(), texD->m_pixelSize)
+ : texD->m_pixelSize;
+ aRb.format = texD->m_format;
+ texD->lastActiveFrameSlot = currentFrameSlot;
+ } else {
+ Q_ASSERT(currentSwapChain);
+ swapChainD = QRHI_RES(QVkSwapChain, currentSwapChain);
+ if (!swapChainD->supportsReadback) {
+ qWarning("Swapchain does not support readback");
+ continue;
+ }
+ aRb.pixelSize = swapChainD->pixelSize;
+ aRb.format = colorTextureFormatFromVkFormat(swapChainD->colorFormat, nullptr);
+ if (aRb.format == QRhiTexture::UnknownFormat)
+ continue;
+
+ // Multisample swapchains need nothing special since resolving
+ // happens when ending a renderpass.
+ }
+ textureFormatInfo(aRb.format, aRb.pixelSize, nullptr, &aRb.bufSize);
+
+ // Create a host visible buffer.
+ VkBufferCreateInfo bufferInfo;
+ memset(&bufferInfo, 0, sizeof(bufferInfo));
+ bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ bufferInfo.size = aRb.bufSize;
+ bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
+
+ VmaAllocationCreateInfo allocInfo;
+ memset(&allocInfo, 0, sizeof(allocInfo));
+ allocInfo.usage = VMA_MEMORY_USAGE_GPU_TO_CPU;
+
+ VmaAllocation allocation;
+ VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo, &aRb.buf, &allocation, nullptr);
+ if (err == VK_SUCCESS) {
+ aRb.bufAlloc = allocation;
+ QRHI_PROF_F(newReadbackBuffer(quint64(aRb.buf),
+ texD ? static_cast<QRhiResource *>(texD) : static_cast<QRhiResource *>(swapChainD),
+ aRb.bufSize));
+ } else {
+ qWarning("Failed to create readback buffer of size %u: %d", aRb.bufSize, err);
+ continue;
+ }
+
+ // Copy from the (optimal and not host visible) image into the buffer.
+ VkBufferImageCopy copyDesc;
+ memset(&copyDesc, 0, sizeof(copyDesc));
+ copyDesc.bufferOffset = 0;
+ copyDesc.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ copyDesc.imageSubresource.mipLevel = u.read.rb.level();
+ copyDesc.imageSubresource.baseArrayLayer = u.read.rb.layer();
+ copyDesc.imageSubresource.layerCount = 1;
+ copyDesc.imageExtent.width = aRb.pixelSize.width();
+ copyDesc.imageExtent.height = aRb.pixelSize.height();
+ copyDesc.imageExtent.depth = 1;
+
+ if (texD) {
+ trackedImageBarrier(cbD, texD, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::CopyImageToBuffer;
+ cmd.args.copyImageToBuffer.src = texD->image;
+ cmd.args.copyImageToBuffer.srcLayout = texD->usageState.layout;
+ cmd.args.copyImageToBuffer.dst = aRb.buf;
+ cmd.args.copyImageToBuffer.desc = copyDesc;
+ cbD->commands.append(cmd);
+ } else {
+ // use the swapchain image
+ QVkSwapChain::ImageResources &imageRes(swapChainD->imageRes[swapChainD->currentImageIndex]);
+ VkImage image = imageRes.image;
+ if (imageRes.lastUse != QVkSwapChain::ImageResources::ScImageUseTransferSource) {
+ if (imageRes.lastUse != QVkSwapChain::ImageResources::ScImageUseRender) {
+ qWarning("Attempted to read back undefined swapchain image content, "
+ "results are undefined. (do a render pass first)");
+ }
+ subresourceBarrier(cbD, image,
+ VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ VK_ACCESS_MEMORY_READ_BIT, VK_ACCESS_TRANSFER_READ_BIT,
+ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
+ 0, 1,
+ 0, 1);
+ imageRes.lastUse = QVkSwapChain::ImageResources::ScImageUseTransferSource;
+ }
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::CopyImageToBuffer;
+ cmd.args.copyImageToBuffer.src = image;
+ cmd.args.copyImageToBuffer.srcLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
+ cmd.args.copyImageToBuffer.dst = aRb.buf;
+ cmd.args.copyImageToBuffer.desc = copyDesc;
+ cbD->commands.append(cmd);
+ }
+
+ activeReadbacks.append(aRb);
+ } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::MipGen) {
+ QVkTexture *utexD = QRHI_RES(QVkTexture, u.mipgen.tex);
+ Q_ASSERT(utexD->m_flags.testFlag(QRhiTexture::UsedWithGenerateMips));
+ int w = utexD->m_pixelSize.width();
+ int h = utexD->m_pixelSize.height();
+
+ VkImageLayout origLayout = utexD->usageState.layout;
+ VkAccessFlags origAccess = utexD->usageState.access;
+ VkPipelineStageFlags origStage = utexD->usageState.stage;
+ if (!origStage)
+ origStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+
+ for (uint level = 1; level < utexD->mipLevelCount; ++level) {
+ if (level == 1) {
+ subresourceBarrier(cbD, utexD->image,
+ origLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ origAccess, VK_ACCESS_TRANSFER_READ_BIT,
+ origStage, VK_PIPELINE_STAGE_TRANSFER_BIT,
+ u.mipgen.layer, 1,
+ level - 1, 1);
+ } else {
+ subresourceBarrier(cbD, utexD->image,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
+ VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
+ u.mipgen.layer, 1,
+ level - 1, 1);
+ }
+
+ subresourceBarrier(cbD, utexD->image,
+ origLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ origAccess, VK_ACCESS_TRANSFER_WRITE_BIT,
+ origStage, VK_PIPELINE_STAGE_TRANSFER_BIT,
+ u.mipgen.layer, 1,
+ level, 1);
+
+ VkImageBlit region;
+ memset(&region, 0, sizeof(region));
+
+ region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ region.srcSubresource.mipLevel = level - 1;
+ region.srcSubresource.baseArrayLayer = u.mipgen.layer;
+ region.srcSubresource.layerCount = 1;
+
+ region.srcOffsets[1].x = qMax(1, w);
+ region.srcOffsets[1].y = qMax(1, h);
+ region.srcOffsets[1].z = 1;
+
+ region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ region.dstSubresource.mipLevel = level;
+ region.dstSubresource.baseArrayLayer = u.mipgen.layer;
+ region.dstSubresource.layerCount = 1;
+
+ region.dstOffsets[1].x = qMax(1, w >> 1);
+ region.dstOffsets[1].y = qMax(1, h >> 1);
+ region.dstOffsets[1].z = 1;
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::BlitImage;
+ cmd.args.blitImage.src = utexD->image;
+ cmd.args.blitImage.srcLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
+ cmd.args.blitImage.dst = utexD->image;
+ cmd.args.blitImage.dstLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+ cmd.args.blitImage.filter = VK_FILTER_LINEAR;
+ cmd.args.blitImage.desc = region;
+ cbD->commands.append(cmd);
+
+ w >>= 1;
+ h >>= 1;
+ }
+
+ if (utexD->mipLevelCount > 1) {
+ subresourceBarrier(cbD, utexD->image,
+ VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, origLayout,
+ VK_ACCESS_TRANSFER_READ_BIT, origAccess,
+ VK_PIPELINE_STAGE_TRANSFER_BIT, origStage,
+ u.mipgen.layer, 1,
+ 0, utexD->mipLevelCount - 1);
+ subresourceBarrier(cbD, utexD->image,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, origLayout,
+ VK_ACCESS_TRANSFER_WRITE_BIT, origAccess,
+ VK_PIPELINE_STAGE_TRANSFER_BIT, origStage,
+ u.mipgen.layer, 1,
+ utexD->mipLevelCount - 1, 1);
+ }
+
+ utexD->lastActiveFrameSlot = currentFrameSlot;
+ }
+ }
+
+ ud->free();
+}
+
+void QRhiVulkan::executeBufferHostWritesForCurrentFrame(QVkBuffer *bufD)
+{
+ QVector<QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate> &updates(bufD->pendingDynamicUpdates[currentFrameSlot]);
+ if (updates.isEmpty())
+ return;
+
+ Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
+ void *p = nullptr;
+ VmaAllocation a = toVmaAllocation(bufD->allocations[currentFrameSlot]);
+ // The vmaMap/Unmap are basically a no-op when persistently mapped since it
+ // refcounts; this is great because we don't need to care if the allocation
+ // was created as persistently mapped or not.
+ VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to map buffer: %d", err);
+ return;
+ }
+ int changeBegin = -1;
+ int changeEnd = -1;
+ for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : updates) {
+ Q_ASSERT(bufD == QRHI_RES(QVkBuffer, u.buf));
+ memcpy(static_cast<char *>(p) + u.offset, u.data.constData(), u.data.size());
+ if (changeBegin == -1 || u.offset < changeBegin)
+ changeBegin = u.offset;
+ if (changeEnd == -1 || u.offset + u.data.size() > changeEnd)
+ changeEnd = u.offset + u.data.size();
+ }
+ vmaUnmapMemory(toVmaAllocator(allocator), a);
+ if (changeBegin >= 0)
+ vmaFlushAllocation(toVmaAllocator(allocator), a, changeBegin, changeEnd - changeBegin);
+
+ updates.clear();
+}
+
+static void qrhivk_releaseBuffer(const QRhiVulkan::DeferredReleaseEntry &e, void *allocator)
+{
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
+ vmaDestroyBuffer(toVmaAllocator(allocator), e.buffer.buffers[i], toVmaAllocation(e.buffer.allocations[i]));
+ vmaDestroyBuffer(toVmaAllocator(allocator), e.buffer.stagingBuffers[i], toVmaAllocation(e.buffer.stagingAllocations[i]));
+ }
+}
+
+static void qrhivk_releaseRenderBuffer(const QRhiVulkan::DeferredReleaseEntry &e, VkDevice dev, QVulkanDeviceFunctions *df)
+{
+ df->vkDestroyImageView(dev, e.renderBuffer.imageView, nullptr);
+ df->vkDestroyImage(dev, e.renderBuffer.image, nullptr);
+ df->vkFreeMemory(dev, e.renderBuffer.memory, nullptr);
+}
+
+static void qrhivk_releaseTexture(const QRhiVulkan::DeferredReleaseEntry &e, VkDevice dev, QVulkanDeviceFunctions *df, void *allocator)
+{
+ df->vkDestroyImageView(dev, e.texture.imageView, nullptr);
+ vmaDestroyImage(toVmaAllocator(allocator), e.texture.image, toVmaAllocation(e.texture.allocation));
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
+ vmaDestroyBuffer(toVmaAllocator(allocator), e.texture.stagingBuffers[i], toVmaAllocation(e.texture.stagingAllocations[i]));
+ for (int i = 0; i < QRhi::MAX_LEVELS; ++i) {
+ if (e.texture.extraImageViews[i])
+ df->vkDestroyImageView(dev, e.texture.extraImageViews[i], nullptr);
+ }
+}
+
+static void qrhivk_releaseSampler(const QRhiVulkan::DeferredReleaseEntry &e, VkDevice dev, QVulkanDeviceFunctions *df)
+{
+ df->vkDestroySampler(dev, e.sampler.sampler, nullptr);
+}
+
+void QRhiVulkan::executeDeferredReleases(bool forced)
+{
+ for (int i = releaseQueue.count() - 1; i >= 0; --i) {
+ const QRhiVulkan::DeferredReleaseEntry &e(releaseQueue[i]);
+ if (forced || currentFrameSlot == e.lastActiveFrameSlot || e.lastActiveFrameSlot < 0) {
+ switch (e.type) {
+ case QRhiVulkan::DeferredReleaseEntry::Pipeline:
+ df->vkDestroyPipeline(dev, e.pipelineState.pipeline, nullptr);
+ df->vkDestroyPipelineLayout(dev, e.pipelineState.layout, nullptr);
+ break;
+ case QRhiVulkan::DeferredReleaseEntry::ShaderResourceBindings:
+ df->vkDestroyDescriptorSetLayout(dev, e.shaderResourceBindings.layout, nullptr);
+ if (e.shaderResourceBindings.poolIndex >= 0) {
+ descriptorPools[e.shaderResourceBindings.poolIndex].refCount -= 1;
+ Q_ASSERT(descriptorPools[e.shaderResourceBindings.poolIndex].refCount >= 0);
+ }
+ break;
+ case QRhiVulkan::DeferredReleaseEntry::Buffer:
+ qrhivk_releaseBuffer(e, allocator);
+ break;
+ case QRhiVulkan::DeferredReleaseEntry::RenderBuffer:
+ qrhivk_releaseRenderBuffer(e, dev, df);
+ break;
+ case QRhiVulkan::DeferredReleaseEntry::Texture:
+ qrhivk_releaseTexture(e, dev, df, allocator);
+ break;
+ case QRhiVulkan::DeferredReleaseEntry::Sampler:
+ qrhivk_releaseSampler(e, dev, df);
+ break;
+ case QRhiVulkan::DeferredReleaseEntry::TextureRenderTarget:
+ df->vkDestroyFramebuffer(dev, e.textureRenderTarget.fb, nullptr);
+ for (int att = 0; att < QVkRenderTargetData::MAX_COLOR_ATTACHMENTS; ++att) {
+ df->vkDestroyImageView(dev, e.textureRenderTarget.rtv[att], nullptr);
+ df->vkDestroyImageView(dev, e.textureRenderTarget.resrtv[att], nullptr);
+ }
+ break;
+ case QRhiVulkan::DeferredReleaseEntry::RenderPass:
+ df->vkDestroyRenderPass(dev, e.renderPass.rp, nullptr);
+ break;
+ case QRhiVulkan::DeferredReleaseEntry::StagingBuffer:
+ vmaDestroyBuffer(toVmaAllocator(allocator), e.stagingBuffer.stagingBuffer, toVmaAllocation(e.stagingBuffer.stagingAllocation));
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ releaseQueue.removeAt(i);
+ }
+ }
+}
+
+void QRhiVulkan::finishActiveReadbacks(bool forced)
+{
+ QVarLengthArray<std::function<void()>, 4> completedCallbacks;
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+
+ for (int i = activeReadbacks.count() - 1; i >= 0; --i) {
+ const QRhiVulkan::ActiveReadback &aRb(activeReadbacks[i]);
+ if (forced || currentFrameSlot == aRb.activeFrameSlot || aRb.activeFrameSlot < 0) {
+ aRb.result->format = aRb.format;
+ aRb.result->pixelSize = aRb.pixelSize;
+ aRb.result->data.resize(aRb.bufSize);
+ void *p = nullptr;
+ VmaAllocation a = toVmaAllocation(aRb.bufAlloc);
+ VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to map readback buffer: %d", err);
+ continue;
+ }
+ memcpy(aRb.result->data.data(), p, aRb.bufSize);
+ vmaUnmapMemory(toVmaAllocator(allocator), a);
+
+ vmaDestroyBuffer(toVmaAllocator(allocator), aRb.buf, a);
+ QRHI_PROF_F(releaseReadbackBuffer(quint64(aRb.buf)));
+
+ if (aRb.result->completed)
+ completedCallbacks.append(aRb.result->completed);
+
+ activeReadbacks.removeAt(i);
+ }
+ }
+
+ for (auto f : completedCallbacks)
+ f();
+}
+
+static struct {
+ VkSampleCountFlagBits mask;
+ int count;
+} qvk_sampleCounts[] = {
+ // keep this sorted by 'count'
+ { VK_SAMPLE_COUNT_1_BIT, 1 },
+ { VK_SAMPLE_COUNT_2_BIT, 2 },
+ { VK_SAMPLE_COUNT_4_BIT, 4 },
+ { VK_SAMPLE_COUNT_8_BIT, 8 },
+ { VK_SAMPLE_COUNT_16_BIT, 16 },
+ { VK_SAMPLE_COUNT_32_BIT, 32 },
+ { VK_SAMPLE_COUNT_64_BIT, 64 }
+};
+
+QVector<int> QRhiVulkan::supportedSampleCounts() const
+{
+ const VkPhysicalDeviceLimits *limits = &physDevProperties.limits;
+ VkSampleCountFlags color = limits->framebufferColorSampleCounts;
+ VkSampleCountFlags depth = limits->framebufferDepthSampleCounts;
+ VkSampleCountFlags stencil = limits->framebufferStencilSampleCounts;
+ QVector<int> result;
+
+ for (size_t i = 0; i < sizeof(qvk_sampleCounts) / sizeof(qvk_sampleCounts[0]); ++i) {
+ if ((color & qvk_sampleCounts[i].mask)
+ && (depth & qvk_sampleCounts[i].mask)
+ && (stencil & qvk_sampleCounts[i].mask))
+ {
+ result.append(qvk_sampleCounts[i].count);
+ }
+ }
+
+ return result;
+}
+
+VkSampleCountFlagBits QRhiVulkan::effectiveSampleCount(int sampleCount)
+{
+ // Stay compatible with QSurfaceFormat and friends where samples == 0 means the same as 1.
+ sampleCount = qBound(1, sampleCount, 64);
+
+ if (!supportedSampleCounts().contains(sampleCount)) {
+ qWarning("Attempted to set unsupported sample count %d", sampleCount);
+ return VK_SAMPLE_COUNT_1_BIT;
+ }
+
+ for (size_t i = 0; i < sizeof(qvk_sampleCounts) / sizeof(qvk_sampleCounts[0]); ++i) {
+ if (qvk_sampleCounts[i].count == sampleCount)
+ return qvk_sampleCounts[i].mask;
+ }
+
+ Q_UNREACHABLE();
+ return VK_SAMPLE_COUNT_1_BIT;
+}
+
+void QRhiVulkan::enqueueTransitionPassResources(QVkCommandBuffer *cbD)
+{
+ cbD->passResTrackers.append(QRhiPassResourceTracker());
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::TransitionPassResources;
+ cmd.args.transitionResources.trackerIndex = cbD->passResTrackers.count() - 1;
+ cbD->commands.append(cmd);
+ cbD->currentPassResTrackerIndex = cbD->passResTrackers.count() - 1;
+}
+
+void QRhiVulkan::recordCommandBuffer(QVkCommandBuffer *cbD)
+{
+ Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass);
+
+ for (QVkCommandBuffer::Command &cmd : cbD->commands) {
+ switch (cmd.cmd) {
+ case QVkCommandBuffer::Command::CopyBuffer:
+ df->vkCmdCopyBuffer(cbD->cb, cmd.args.copyBuffer.src, cmd.args.copyBuffer.dst,
+ 1, &cmd.args.copyBuffer.desc);
+ break;
+ case QVkCommandBuffer::Command::CopyBufferToImage:
+ df->vkCmdCopyBufferToImage(cbD->cb, cmd.args.copyBufferToImage.src, cmd.args.copyBufferToImage.dst,
+ cmd.args.copyBufferToImage.dstLayout,
+ cmd.args.copyBufferToImage.count,
+ cbD->pools.bufferImageCopy.constData() + cmd.args.copyBufferToImage.bufferImageCopyIndex);
+ break;
+ case QVkCommandBuffer::Command::CopyImage:
+ df->vkCmdCopyImage(cbD->cb, cmd.args.copyImage.src, cmd.args.copyImage.srcLayout,
+ cmd.args.copyImage.dst, cmd.args.copyImage.dstLayout,
+ 1, &cmd.args.copyImage.desc);
+ break;
+ case QVkCommandBuffer::Command::CopyImageToBuffer:
+ df->vkCmdCopyImageToBuffer(cbD->cb, cmd.args.copyImageToBuffer.src, cmd.args.copyImageToBuffer.srcLayout,
+ cmd.args.copyImageToBuffer.dst,
+ 1, &cmd.args.copyImageToBuffer.desc);
+ break;
+ case QVkCommandBuffer::Command::ImageBarrier:
+ df->vkCmdPipelineBarrier(cbD->cb, cmd.args.imageBarrier.srcStageMask, cmd.args.imageBarrier.dstStageMask,
+ 0, 0, nullptr, 0, nullptr,
+ 1, &cmd.args.imageBarrier.desc);
+ break;
+ case QVkCommandBuffer::Command::BufferBarrier:
+ df->vkCmdPipelineBarrier(cbD->cb, cmd.args.bufferBarrier.srcStageMask, cmd.args.bufferBarrier.dstStageMask,
+ 0, 0, nullptr,
+ 1, &cmd.args.bufferBarrier.desc,
+ 0, nullptr);
+ break;
+ case QVkCommandBuffer::Command::BlitImage:
+ df->vkCmdBlitImage(cbD->cb, cmd.args.blitImage.src, cmd.args.blitImage.srcLayout,
+ cmd.args.blitImage.dst, cmd.args.blitImage.dstLayout,
+ 1, &cmd.args.blitImage.desc,
+ cmd.args.blitImage.filter);
+ break;
+ case QVkCommandBuffer::Command::BeginRenderPass:
+ cmd.args.beginRenderPass.desc.pClearValues = cbD->pools.clearValue.constData() + cmd.args.beginRenderPass.clearValueIndex;
+ df->vkCmdBeginRenderPass(cbD->cb, &cmd.args.beginRenderPass.desc, VK_SUBPASS_CONTENTS_INLINE);
+ break;
+ case QVkCommandBuffer::Command::EndRenderPass:
+ df->vkCmdEndRenderPass(cbD->cb);
+ break;
+ case QVkCommandBuffer::Command::BindPipeline:
+ df->vkCmdBindPipeline(cbD->cb, cmd.args.bindPipeline.bindPoint, cmd.args.bindPipeline.pipeline);
+ break;
+ case QVkCommandBuffer::Command::BindDescriptorSet:
+ {
+ const uint32_t *offsets = nullptr;
+ if (cmd.args.bindDescriptorSet.dynamicOffsetCount > 0)
+ offsets = cbD->pools.dynamicOffset.constData() + cmd.args.bindDescriptorSet.dynamicOffsetIndex;
+ df->vkCmdBindDescriptorSets(cbD->cb, cmd.args.bindDescriptorSet.bindPoint,
+ cmd.args.bindDescriptorSet.pipelineLayout,
+ 0, 1, &cmd.args.bindDescriptorSet.descSet,
+ cmd.args.bindDescriptorSet.dynamicOffsetCount,
+ offsets);
+ }
+ break;
+ case QVkCommandBuffer::Command::BindVertexBuffer:
+ df->vkCmdBindVertexBuffers(cbD->cb, cmd.args.bindVertexBuffer.startBinding,
+ cmd.args.bindVertexBuffer.count,
+ cbD->pools.vertexBuffer.constData() + cmd.args.bindVertexBuffer.vertexBufferIndex,
+ cbD->pools.vertexBufferOffset.constData() + cmd.args.bindVertexBuffer.vertexBufferOffsetIndex);
+ break;
+ case QVkCommandBuffer::Command::BindIndexBuffer:
+ df->vkCmdBindIndexBuffer(cbD->cb, cmd.args.bindIndexBuffer.buf,
+ cmd.args.bindIndexBuffer.ofs, cmd.args.bindIndexBuffer.type);
+ break;
+ case QVkCommandBuffer::Command::SetViewport:
+ df->vkCmdSetViewport(cbD->cb, 0, 1, &cmd.args.setViewport.viewport);
+ break;
+ case QVkCommandBuffer::Command::SetScissor:
+ df->vkCmdSetScissor(cbD->cb, 0, 1, &cmd.args.setScissor.scissor);
+ break;
+ case QVkCommandBuffer::Command::SetBlendConstants:
+ df->vkCmdSetBlendConstants(cbD->cb, cmd.args.setBlendConstants.c);
+ break;
+ case QVkCommandBuffer::Command::SetStencilRef:
+ df->vkCmdSetStencilReference(cbD->cb, VK_STENCIL_FRONT_AND_BACK, cmd.args.setStencilRef.ref);
+ break;
+ case QVkCommandBuffer::Command::Draw:
+ df->vkCmdDraw(cbD->cb, cmd.args.draw.vertexCount, cmd.args.draw.instanceCount,
+ cmd.args.draw.firstVertex, cmd.args.draw.firstInstance);
+ break;
+ case QVkCommandBuffer::Command::DrawIndexed:
+ df->vkCmdDrawIndexed(cbD->cb, cmd.args.drawIndexed.indexCount, cmd.args.drawIndexed.instanceCount,
+ cmd.args.drawIndexed.firstIndex, cmd.args.drawIndexed.vertexOffset,
+ cmd.args.drawIndexed.firstInstance);
+ break;
+ case QVkCommandBuffer::Command::DebugMarkerBegin:
+ cmd.args.debugMarkerBegin.marker.pMarkerName =
+ cbD->pools.debugMarkerName[cmd.args.debugMarkerBegin.markerNameIndex].constData();
+ vkCmdDebugMarkerBegin(cbD->cb, &cmd.args.debugMarkerBegin.marker);
+ break;
+ case QVkCommandBuffer::Command::DebugMarkerEnd:
+ vkCmdDebugMarkerEnd(cbD->cb);
+ break;
+ case QVkCommandBuffer::Command::DebugMarkerInsert:
+ vkCmdDebugMarkerInsert(cbD->cb, &cmd.args.debugMarkerInsert.marker);
+ break;
+ case QVkCommandBuffer::Command::TransitionPassResources:
+ recordTransitionPassResources(cbD, cbD->passResTrackers[cmd.args.transitionResources.trackerIndex]);
+ break;
+ case QVkCommandBuffer::Command::Dispatch:
+ df->vkCmdDispatch(cbD->cb, cmd.args.dispatch.x, cmd.args.dispatch.y, cmd.args.dispatch.z);
+ break;
+ default:
+ break;
+ }
+ }
+
+ cbD->resetCommands();
+}
+
+static inline VkAccessFlags toVkAccess(QRhiPassResourceTracker::BufferAccess access)
+{
+ switch (access) {
+ case QRhiPassResourceTracker::BufVertexInput:
+ return VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
+ case QRhiPassResourceTracker::BufIndexRead:
+ return VK_ACCESS_INDEX_READ_BIT;
+ case QRhiPassResourceTracker::BufUniformRead:
+ return VK_ACCESS_UNIFORM_READ_BIT;
+ case QRhiPassResourceTracker::BufStorageLoad:
+ return VK_ACCESS_SHADER_READ_BIT;
+ case QRhiPassResourceTracker::BufStorageStore:
+ return VK_ACCESS_SHADER_WRITE_BIT;
+ case QRhiPassResourceTracker::BufStorageLoadStore:
+ return VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ return 0;
+}
+
+static inline VkPipelineStageFlags toVkPipelineStage(QRhiPassResourceTracker::BufferStage stage)
+{
+ switch (stage) {
+ case QRhiPassResourceTracker::BufVertexInputStage:
+ return VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
+ case QRhiPassResourceTracker::BufVertexStage:
+ return VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
+ case QRhiPassResourceTracker::BufFragmentStage:
+ return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+ case QRhiPassResourceTracker::BufComputeStage:
+ return VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ return 0;
+}
+
+static inline QVkBuffer::UsageState toVkBufferUsageState(QRhiPassResourceTracker::UsageState usage)
+{
+ QVkBuffer::UsageState u;
+ u.access = usage.access;
+ u.stage = usage.stage;
+ return u;
+}
+
+static inline VkImageLayout toVkLayout(QRhiPassResourceTracker::TextureAccess access)
+{
+ switch (access) {
+ case QRhiPassResourceTracker::TexSample:
+ return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ case QRhiPassResourceTracker::TexColorOutput:
+ return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ case QRhiPassResourceTracker::TexDepthOutput:
+ return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+ case QRhiPassResourceTracker::TexStorageLoad:
+ Q_FALLTHROUGH();
+ case QRhiPassResourceTracker::TexStorageStore:
+ Q_FALLTHROUGH();
+ case QRhiPassResourceTracker::TexStorageLoadStore:
+ return VK_IMAGE_LAYOUT_GENERAL;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ return VK_IMAGE_LAYOUT_GENERAL;
+}
+
+static inline VkAccessFlags toVkAccess(QRhiPassResourceTracker::TextureAccess access)
+{
+ switch (access) {
+ case QRhiPassResourceTracker::TexSample:
+ return VK_ACCESS_SHADER_READ_BIT;
+ case QRhiPassResourceTracker::TexColorOutput:
+ return VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ case QRhiPassResourceTracker::TexDepthOutput:
+ return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
+ case QRhiPassResourceTracker::TexStorageLoad:
+ return VK_ACCESS_SHADER_READ_BIT;
+ case QRhiPassResourceTracker::TexStorageStore:
+ return VK_ACCESS_SHADER_WRITE_BIT;
+ case QRhiPassResourceTracker::TexStorageLoadStore:
+ return VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ return 0;
+}
+
+static inline VkPipelineStageFlags toVkPipelineStage(QRhiPassResourceTracker::TextureStage stage)
+{
+ switch (stage) {
+ case QRhiPassResourceTracker::TexVertexStage:
+ return VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
+ case QRhiPassResourceTracker::TexFragmentStage:
+ return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+ case QRhiPassResourceTracker::TexColorOutputStage:
+ return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ case QRhiPassResourceTracker::TexDepthOutputStage:
+ return VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
+ case QRhiPassResourceTracker::TexComputeStage:
+ return VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ return 0;
+}
+
+static inline QVkTexture::UsageState toVkTextureUsageState(QRhiPassResourceTracker::UsageState usage)
+{
+ QVkTexture::UsageState u;
+ u.layout = VkImageLayout(usage.layout);
+ u.access = usage.access;
+ u.stage = usage.stage;
+ return u;
+}
+
+void QRhiVulkan::trackedRegisterBuffer(QRhiPassResourceTracker *passResTracker,
+ QVkBuffer *bufD,
+ int slot,
+ QRhiPassResourceTracker::BufferAccess access,
+ QRhiPassResourceTracker::BufferStage stage)
+{
+ QVkBuffer::UsageState &u(bufD->usageState[slot]);
+ passResTracker->registerBuffer(bufD, slot, &access, &stage, toPassTrackerUsageState(u));
+ u.access = toVkAccess(access);
+ u.stage = toVkPipelineStage(stage);
+}
+
+void QRhiVulkan::trackedRegisterTexture(QRhiPassResourceTracker *passResTracker,
+ QVkTexture *texD,
+ QRhiPassResourceTracker::TextureAccess access,
+ QRhiPassResourceTracker::TextureStage stage)
+{
+ QVkTexture::UsageState &u(texD->usageState);
+ passResTracker->registerTexture(texD, &access, &stage, toPassTrackerUsageState(u));
+ u.layout = toVkLayout(access);
+ u.access = toVkAccess(access);
+ u.stage = toVkPipelineStage(stage);
+}
+
+void QRhiVulkan::recordTransitionPassResources(QVkCommandBuffer *cbD, const QRhiPassResourceTracker &tracker)
+{
+ if (tracker.isEmpty())
+ return;
+
+ const QVector<QRhiPassResourceTracker::Buffer> *buffers = tracker.buffers();
+ for (const QRhiPassResourceTracker::Buffer &b : *buffers) {
+ QVkBuffer *bufD = QRHI_RES(QVkBuffer, b.buf);
+ VkAccessFlags access = toVkAccess(b.access);
+ VkPipelineStageFlags stage = toVkPipelineStage(b.stage);
+ QVkBuffer::UsageState s = toVkBufferUsageState(b.stateAtPassBegin);
+ if (!s.stage)
+ continue;
+ if (s.access == access && s.stage == stage) {
+ if (!accessIsWrite(access))
+ continue;
+ }
+ VkBufferMemoryBarrier bufMemBarrier;
+ memset(&bufMemBarrier, 0, sizeof(bufMemBarrier));
+ bufMemBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
+ bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ bufMemBarrier.srcAccessMask = s.access;
+ bufMemBarrier.dstAccessMask = access;
+ bufMemBarrier.buffer = bufD->buffers[b.slot];
+ bufMemBarrier.size = VK_WHOLE_SIZE;
+ df->vkCmdPipelineBarrier(cbD->cb, s.stage, stage, 0,
+ 0, nullptr,
+ 1, &bufMemBarrier,
+ 0, nullptr);
+ }
+
+ const QVector<QRhiPassResourceTracker::Texture> *textures = tracker.textures();
+ for (const QRhiPassResourceTracker::Texture &t : *textures) {
+ QVkTexture *texD = QRHI_RES(QVkTexture, t.tex);
+ VkImageLayout layout = toVkLayout(t.access);
+ VkAccessFlags access = toVkAccess(t.access);
+ VkPipelineStageFlags stage = toVkPipelineStage(t.stage);
+ QVkTexture::UsageState s = toVkTextureUsageState(t.stateAtPassBegin);
+ if (s.access == access && s.stage == stage && s.layout == layout) {
+ if (!accessIsWrite(access))
+ continue;
+ }
+ VkImageMemoryBarrier barrier;
+ memset(&barrier, 0, sizeof(barrier));
+ barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ barrier.subresourceRange.aspectMask = !isDepthTextureFormat(texD->m_format)
+ ? VK_IMAGE_ASPECT_COLOR_BIT : VK_IMAGE_ASPECT_DEPTH_BIT;
+ barrier.subresourceRange.baseMipLevel = 0;
+ barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
+ barrier.subresourceRange.baseArrayLayer = 0;
+ barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
+ barrier.oldLayout = s.layout; // new textures have this set to PREINITIALIZED
+ barrier.newLayout = layout;
+ barrier.srcAccessMask = s.access; // may be 0 but that's fine
+ barrier.dstAccessMask = access;
+ barrier.image = texD->image;
+ VkPipelineStageFlags srcStage = s.stage;
+ // stage mask cannot be 0
+ if (!srcStage)
+ srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+ df->vkCmdPipelineBarrier(cbD->cb, srcStage, stage, 0,
+ 0, nullptr,
+ 0, nullptr,
+ 1, &barrier);
+ }
+}
+
+QRhiSwapChain *QRhiVulkan::createSwapChain()
+{
+ return new QVkSwapChain(this);
+}
+
+QRhiBuffer *QRhiVulkan::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size)
+{
+ return new QVkBuffer(this, type, usage, size);
+}
+
+int QRhiVulkan::ubufAlignment() const
+{
+ return ubufAlign; // typically 256 (bytes)
+}
+
+bool QRhiVulkan::isYUpInFramebuffer() const
+{
+ return false;
+}
+
+bool QRhiVulkan::isYUpInNDC() const
+{
+ return false;
+}
+
+bool QRhiVulkan::isClipDepthZeroToOne() const
+{
+ return true;
+}
+
+QMatrix4x4 QRhiVulkan::clipSpaceCorrMatrix() const
+{
+ // See https://matthewwellings.com/blog/the-new-vulkan-coordinate-system/
+
+ static QMatrix4x4 m;
+ if (m.isIdentity()) {
+ // NB the ctor takes row-major
+ m = QMatrix4x4(1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, -1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 0.5f, 0.5f,
+ 0.0f, 0.0f, 0.0f, 1.0f);
+ }
+ return m;
+}
+
+bool QRhiVulkan::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const
+{
+ VkPhysicalDeviceFeatures features;
+ f->vkGetPhysicalDeviceFeatures(physDev, &features);
+
+ // Note that with some SDKs the validation layer gives an odd warning about
+ // BC not being supported, even when our check here succeeds. Not much we
+ // can do about that.
+ if (format >= QRhiTexture::BC1 && format <= QRhiTexture::BC7) {
+ if (!features.textureCompressionBC)
+ return false;
+ }
+
+ if (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ETC2_RGBA8) {
+ if (!features.textureCompressionETC2)
+ return false;
+ }
+
+ if (format >= QRhiTexture::ASTC_4x4 && format <= QRhiTexture::ASTC_12x12) {
+ if (!features.textureCompressionASTC_LDR)
+ return false;
+ }
+
+ VkFormat vkformat = toVkTextureFormat(format, flags);
+ VkFormatProperties props;
+ f->vkGetPhysicalDeviceFormatProperties(physDev, vkformat, &props);
+ return (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) != 0;
+}
+
+bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
+{
+ switch (feature) {
+ case QRhi::MultisampleTexture:
+ return true;
+ case QRhi::MultisampleRenderBuffer:
+ return true;
+ case QRhi::DebugMarkers:
+ return debugMarkersAvailable;
+ case QRhi::Timestamps:
+ return timestampValidBits != 0;
+ case QRhi::Instancing:
+ return true;
+ case QRhi::CustomInstanceStepRate:
+ return vertexAttribDivisorAvailable;
+ case QRhi::PrimitiveRestart:
+ return true;
+ case QRhi::NonDynamicUniformBuffers:
+ return true;
+ case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
+ return true;
+ case QRhi::NPOTTextureRepeat:
+ return true;
+ case QRhi::RedOrAlpha8IsRed:
+ return true;
+ case QRhi::ElementIndexUint:
+ return true;
+ case QRhi::Compute:
+ return hasCompute;
+ default:
+ Q_UNREACHABLE();
+ return false;
+ }
+}
+
+int QRhiVulkan::resourceLimit(QRhi::ResourceLimit limit) const
+{
+ switch (limit) {
+ case QRhi::TextureSizeMin:
+ return 1;
+ case QRhi::TextureSizeMax:
+ return physDevProperties.limits.maxImageDimension2D;
+ case QRhi::MaxColorAttachments:
+ return physDevProperties.limits.maxColorAttachments;
+ case QRhi::FramesInFlight:
+ return QVK_FRAMES_IN_FLIGHT;
+ default:
+ Q_UNREACHABLE();
+ return 0;
+ }
+}
+
+const QRhiNativeHandles *QRhiVulkan::nativeHandles()
+{
+ return &nativeHandlesStruct;
+}
+
+void QRhiVulkan::sendVMemStatsToProfiler()
+{
+ QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
+ if (!rhiP)
+ return;
+
+ VmaStats stats;
+ vmaCalculateStats(toVmaAllocator(allocator), &stats);
+ QRHI_PROF_F(vmemStat(stats.total.blockCount, stats.total.allocationCount,
+ stats.total.usedBytes, stats.total.unusedBytes));
+}
+
+void QRhiVulkan::makeThreadLocalNativeContextCurrent()
+{
+ // nothing to do here
+}
+
+QRhiRenderBuffer *QRhiVulkan::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
+ int sampleCount, QRhiRenderBuffer::Flags flags)
+{
+ return new QVkRenderBuffer(this, type, pixelSize, sampleCount, flags);
+}
+
+QRhiTexture *QRhiVulkan::createTexture(QRhiTexture::Format format, const QSize &pixelSize,
+ int sampleCount, QRhiTexture::Flags flags)
+{
+ return new QVkTexture(this, format, pixelSize, sampleCount, flags);
+}
+
+QRhiSampler *QRhiVulkan::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler::AddressMode u, QRhiSampler::AddressMode v)
+{
+ return new QVkSampler(this, magFilter, minFilter, mipmapMode, u, v);
+}
+
+QRhiTextureRenderTarget *QRhiVulkan::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
+ QRhiTextureRenderTarget::Flags flags)
+{
+ return new QVkTextureRenderTarget(this, desc, flags);
+}
+
+QRhiGraphicsPipeline *QRhiVulkan::createGraphicsPipeline()
+{
+ return new QVkGraphicsPipeline(this);
+}
+
+QRhiComputePipeline *QRhiVulkan::createComputePipeline()
+{
+ return new QVkComputePipeline(this);
+}
+
+QRhiShaderResourceBindings *QRhiVulkan::createShaderResourceBindings()
+{
+ return new QVkShaderResourceBindings(this);
+}
+
+void QRhiVulkan::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps)
+{
+ QVkGraphicsPipeline *psD = QRHI_RES(QVkGraphicsPipeline, ps);
+ Q_ASSERT(psD->pipeline);
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
+
+ if (cbD->currentGraphicsPipeline != ps || cbD->currentPipelineGeneration != psD->generation) {
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::BindPipeline;
+ cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+ cmd.args.bindPipeline.pipeline = psD->pipeline;
+ cbD->commands.append(cmd);
+
+ cbD->currentGraphicsPipeline = ps;
+ cbD->currentComputePipeline = nullptr;
+ cbD->currentPipelineGeneration = psD->generation;
+ }
+
+ psD->lastActiveFrameSlot = currentFrameSlot;
+}
+
+QRhiPassResourceTracker::BufferStage toPassTrackerBufferStage(QRhiShaderResourceBinding::StageFlags stages)
+{
+ // pick the earlier stage (as this is going to be dstAccessMask)
+ if (stages.testFlag(QRhiShaderResourceBinding::VertexStage))
+ return QRhiPassResourceTracker::BufVertexStage;
+ if (stages.testFlag(QRhiShaderResourceBinding::FragmentStage))
+ return QRhiPassResourceTracker::BufFragmentStage;
+ if (stages.testFlag(QRhiShaderResourceBinding::ComputeStage))
+ return QRhiPassResourceTracker::BufComputeStage;
+
+ Q_UNREACHABLE();
+ return QRhiPassResourceTracker::BufVertexStage;
+}
+
+QRhiPassResourceTracker::TextureStage toPassTrackerTextureStage(QRhiShaderResourceBinding::StageFlags stages)
+{
+ // pick the earlier stage (as this is going to be dstAccessMask)
+ if (stages.testFlag(QRhiShaderResourceBinding::VertexStage))
+ return QRhiPassResourceTracker::TexVertexStage;
+ if (stages.testFlag(QRhiShaderResourceBinding::FragmentStage))
+ return QRhiPassResourceTracker::TexFragmentStage;
+ if (stages.testFlag(QRhiShaderResourceBinding::ComputeStage))
+ return QRhiPassResourceTracker::TexComputeStage;
+
+ Q_UNREACHABLE();
+ return QRhiPassResourceTracker::TexVertexStage;
+}
+
+void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
+{
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass != QVkCommandBuffer::NoPass);
+ QVkGraphicsPipeline *gfxPsD = QRHI_RES(QVkGraphicsPipeline, cbD->currentGraphicsPipeline);
+ QVkComputePipeline *compPsD = QRHI_RES(QVkComputePipeline, cbD->currentComputePipeline);
+
+ if (!srb) {
+ if (gfxPsD)
+ srb = gfxPsD->m_shaderResourceBindings;
+ else
+ srb = compPsD->m_shaderResourceBindings;
+ }
+
+ QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, srb);
+ bool hasSlottedResourceInSrb = false;
+ bool hasDynamicOffsetInSrb = false;
+
+ for (const QRhiShaderResourceBinding &binding : qAsConst(srbD->sortedBindings)) {
+ const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&binding);
+ switch (b->type) {
+ case QRhiShaderResourceBinding::UniformBuffer:
+ if (QRHI_RES(QVkBuffer, b->u.ubuf.buf)->m_type == QRhiBuffer::Dynamic)
+ hasSlottedResourceInSrb = true;
+ if (b->u.ubuf.hasDynamicOffset)
+ hasDynamicOffsetInSrb = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ const int descSetIdx = hasSlottedResourceInSrb ? currentFrameSlot : 0;
+ bool rewriteDescSet = false;
+
+ // Do host writes and mark referenced shader resources as in-use.
+ // Also prepare to ensure the descriptor set we are going to bind refers to up-to-date Vk objects.
+ for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
+ const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&srbD->sortedBindings[i]);
+ QVkShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[descSetIdx][i]);
+ QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
+ switch (b->type) {
+ case QRhiShaderResourceBinding::UniformBuffer:
+ {
+ QVkBuffer *bufD = QRHI_RES(QVkBuffer, b->u.ubuf.buf);
+ Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer));
+
+ if (bufD->m_type == QRhiBuffer::Dynamic)
+ executeBufferHostWritesForCurrentFrame(bufD);
+
+ bufD->lastActiveFrameSlot = currentFrameSlot;
+ trackedRegisterBuffer(&passResTracker, bufD, bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0,
+ QRhiPassResourceTracker::BufUniformRead,
+ toPassTrackerBufferStage(b->stage));
+
+ // Check both the "local" id (the generation counter) and the
+ // global id. The latter is relevant when a newly allocated
+ // QRhiResource ends up with the same pointer as a previous one.
+ // (and that previous one could have been in an srb...)
+ if (bufD->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) {
+ rewriteDescSet = true;
+ bd.ubuf.id = bufD->m_id;
+ bd.ubuf.generation = bufD->generation;
+ }
+ }
+ break;
+ case QRhiShaderResourceBinding::SampledTexture:
+ {
+ QVkTexture *texD = QRHI_RES(QVkTexture, b->u.stex.tex);
+ QVkSampler *samplerD = QRHI_RES(QVkSampler, b->u.stex.sampler);
+ texD->lastActiveFrameSlot = currentFrameSlot;
+ samplerD->lastActiveFrameSlot = currentFrameSlot;
+ trackedRegisterTexture(&passResTracker, texD,
+ QRhiPassResourceTracker::TexSample,
+ toPassTrackerTextureStage(b->stage));
+
+ if (texD->generation != bd.stex.texGeneration
+ || texD->m_id != bd.stex.texId
+ || samplerD->generation != bd.stex.samplerGeneration
+ || samplerD->m_id != bd.stex.samplerId)
+ {
+ rewriteDescSet = true;
+ bd.stex.texId = texD->m_id;
+ bd.stex.texGeneration = texD->generation;
+ bd.stex.samplerId = samplerD->m_id;
+ bd.stex.samplerGeneration = samplerD->generation;
+ }
+ }
+ break;
+ case QRhiShaderResourceBinding::ImageLoad:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::ImageStore:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::ImageLoadStore:
+ {
+ QVkTexture *texD = QRHI_RES(QVkTexture, b->u.simage.tex);
+ Q_ASSERT(texD->m_flags.testFlag(QRhiTexture::UsedWithLoadStore));
+ texD->lastActiveFrameSlot = currentFrameSlot;
+ QRhiPassResourceTracker::TextureAccess access;
+ if (b->type == QRhiShaderResourceBinding::ImageLoad)
+ access = QRhiPassResourceTracker::TexStorageLoad;
+ else if (b->type == QRhiShaderResourceBinding::ImageStore)
+ access = QRhiPassResourceTracker::TexStorageStore;
+ else
+ access = QRhiPassResourceTracker::TexStorageLoadStore;
+ trackedRegisterTexture(&passResTracker, texD,
+ access,
+ toPassTrackerTextureStage(b->stage));
+
+ if (texD->generation != bd.simage.generation || texD->m_id != bd.simage.id) {
+ rewriteDescSet = true;
+ bd.simage.id = texD->m_id;
+ bd.simage.generation = texD->generation;
+ }
+ }
+ break;
+ case QRhiShaderResourceBinding::BufferLoad:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::BufferStore:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::BufferLoadStore:
+ {
+ QVkBuffer *bufD = QRHI_RES(QVkBuffer, b->u.sbuf.buf);
+ Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer));
+
+ if (bufD->m_type == QRhiBuffer::Dynamic)
+ executeBufferHostWritesForCurrentFrame(bufD);
+
+ bufD->lastActiveFrameSlot = currentFrameSlot;
+ QRhiPassResourceTracker::BufferAccess access;
+ if (b->type == QRhiShaderResourceBinding::BufferLoad)
+ access = QRhiPassResourceTracker::BufStorageLoad;
+ else if (b->type == QRhiShaderResourceBinding::BufferStore)
+ access = QRhiPassResourceTracker::BufStorageStore;
+ else
+ access = QRhiPassResourceTracker::BufStorageLoadStore;
+ trackedRegisterBuffer(&passResTracker, bufD, bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0,
+ access,
+ toPassTrackerBufferStage(b->stage));
+
+ if (bufD->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) {
+ rewriteDescSet = true;
+ bd.sbuf.id = bufD->m_id;
+ bd.sbuf.generation = bufD->generation;
+ }
+ }
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+
+ // write descriptor sets, if needed
+ if (rewriteDescSet)
+ updateShaderResourceBindings(srb, descSetIdx);
+
+ // make sure the descriptors for the correct slot will get bound.
+ // also, dynamic offsets always need a bind.
+ const bool forceRebind = (hasSlottedResourceInSrb && cbD->currentDescSetSlot != descSetIdx) || hasDynamicOffsetInSrb;
+
+ const bool srbChanged = gfxPsD ? (cbD->currentGraphicsSrb != srb) : (cbD->currentComputeSrb != srb);
+
+ if (forceRebind || rewriteDescSet || srbChanged || cbD->currentSrbGeneration != srbD->generation) {
+ QVarLengthArray<uint32_t, 4> dynOfs;
+ if (hasDynamicOffsetInSrb) {
+ // Filling out dynOfs based on the sorted bindings is important
+ // because dynOfs has to be ordered based on the binding numbers,
+ // and neither srb nor dynamicOffsets has any such ordering
+ // requirement.
+ for (const QRhiShaderResourceBinding &binding : qAsConst(srbD->sortedBindings)) {
+ const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&binding);
+ if (b->type == QRhiShaderResourceBinding::UniformBuffer && b->u.ubuf.hasDynamicOffset) {
+ uint32_t offset = 0;
+ for (int i = 0; i < dynamicOffsetCount; ++i) {
+ const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
+ if (dynOfs.first == b->binding) {
+ offset = dynOfs.second;
+ break;
+ }
+ }
+ dynOfs.append(offset); // use 0 if dynamicOffsets did not contain this binding
+ }
+ }
+ }
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::BindDescriptorSet;
+ cmd.args.bindDescriptorSet.bindPoint = gfxPsD ? VK_PIPELINE_BIND_POINT_GRAPHICS
+ : VK_PIPELINE_BIND_POINT_COMPUTE;
+ cmd.args.bindDescriptorSet.pipelineLayout = gfxPsD ? gfxPsD->layout : compPsD->layout;
+ cmd.args.bindDescriptorSet.descSet = srbD->descSets[descSetIdx];
+ cmd.args.bindDescriptorSet.dynamicOffsetCount = dynOfs.count();
+ cmd.args.bindDescriptorSet.dynamicOffsetIndex = cbD->pools.dynamicOffset.count();
+ cbD->pools.dynamicOffset.append(dynOfs.constData(), dynOfs.count());
+ cbD->commands.append(cmd);
+
+ if (gfxPsD) {
+ cbD->currentGraphicsSrb = srb;
+ cbD->currentComputeSrb = nullptr;
+ } else {
+ cbD->currentGraphicsSrb = nullptr;
+ cbD->currentComputeSrb = srb;
+ }
+ cbD->currentSrbGeneration = srbD->generation;
+ cbD->currentDescSetSlot = descSetIdx;
+ }
+
+ srbD->lastActiveFrameSlot = currentFrameSlot;
+}
+
+void QRhiVulkan::setVertexInput(QRhiCommandBuffer *cb,
+ int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
+ QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
+{
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
+ QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
+
+ bool needsBindVBuf = false;
+ for (int i = 0; i < bindingCount; ++i) {
+ const int inputSlot = startBinding + i;
+ QVkBuffer *bufD = QRHI_RES(QVkBuffer, bindings[i].first);
+ Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::VertexBuffer));
+ bufD->lastActiveFrameSlot = currentFrameSlot;
+ if (bufD->m_type == QRhiBuffer::Dynamic)
+ executeBufferHostWritesForCurrentFrame(bufD);
+
+ const VkBuffer vkvertexbuf = bufD->buffers[bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0];
+ if (cbD->currentVertexBuffers[inputSlot] != vkvertexbuf
+ || cbD->currentVertexOffsets[inputSlot] != bindings[i].second)
+ {
+ needsBindVBuf = true;
+ cbD->currentVertexBuffers[inputSlot] = vkvertexbuf;
+ cbD->currentVertexOffsets[inputSlot] = bindings[i].second;
+ }
+ }
+
+ if (needsBindVBuf) {
+ QVarLengthArray<VkBuffer, 4> bufs;
+ QVarLengthArray<VkDeviceSize, 4> ofs;
+ for (int i = 0; i < bindingCount; ++i) {
+ QVkBuffer *bufD = QRHI_RES(QVkBuffer, bindings[i].first);
+ const int slot = bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0;
+ bufs.append(bufD->buffers[slot]);
+ ofs.append(bindings[i].second);
+ trackedRegisterBuffer(&passResTracker, bufD, slot,
+ QRhiPassResourceTracker::BufVertexInput,
+ QRhiPassResourceTracker::BufVertexInputStage);
+ }
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::BindVertexBuffer;
+ cmd.args.bindVertexBuffer.startBinding = startBinding;
+ cmd.args.bindVertexBuffer.count = bufs.count();
+ cmd.args.bindVertexBuffer.vertexBufferIndex = cbD->pools.vertexBuffer.count();
+ cbD->pools.vertexBuffer.append(bufs.constData(), bufs.count());
+ cmd.args.bindVertexBuffer.vertexBufferOffsetIndex = cbD->pools.vertexBufferOffset.count();
+ cbD->pools.vertexBufferOffset.append(ofs.constData(), ofs.count());
+ cbD->commands.append(cmd);
+ }
+
+ if (indexBuf) {
+ QVkBuffer *ibufD = QRHI_RES(QVkBuffer, indexBuf);
+ Q_ASSERT(ibufD->m_usage.testFlag(QRhiBuffer::IndexBuffer));
+ ibufD->lastActiveFrameSlot = currentFrameSlot;
+ if (ibufD->m_type == QRhiBuffer::Dynamic)
+ executeBufferHostWritesForCurrentFrame(ibufD);
+
+ const int slot = ibufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0;
+ const VkBuffer vkindexbuf = ibufD->buffers[slot];
+ const VkIndexType type = indexFormat == QRhiCommandBuffer::IndexUInt16 ? VK_INDEX_TYPE_UINT16
+ : VK_INDEX_TYPE_UINT32;
+
+ if (cbD->currentIndexBuffer != vkindexbuf
+ || cbD->currentIndexOffset != indexOffset
+ || cbD->currentIndexFormat != type)
+ {
+ cbD->currentIndexBuffer = vkindexbuf;
+ cbD->currentIndexOffset = indexOffset;
+ cbD->currentIndexFormat = type;
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::BindIndexBuffer;
+ cmd.args.bindIndexBuffer.buf = vkindexbuf;
+ cmd.args.bindIndexBuffer.ofs = indexOffset;
+ cmd.args.bindIndexBuffer.type = type;
+ cbD->commands.append(cmd);
+
+ trackedRegisterBuffer(&passResTracker, ibufD, slot,
+ QRhiPassResourceTracker::BufIndexRead,
+ QRhiPassResourceTracker::BufVertexInputStage);
+ }
+ }
+}
+
+void QRhiVulkan::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport)
+{
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
+ const QSize outputSize = cbD->currentTarget->pixelSize();
+
+ // x,y is top-left in VkViewport but bottom-left in QRhiViewport
+ float x, y, w, h;
+ if (!qrhi_toTopLeftRenderTargetRect(outputSize, viewport.viewport(), &x, &y, &w, &h))
+ return;
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::SetViewport;
+ VkViewport *vp = &cmd.args.setViewport.viewport;
+ vp->x = x;
+ vp->y = y;
+ vp->width = w;
+ vp->height = h;
+ vp->minDepth = viewport.minDepth();
+ vp->maxDepth = viewport.maxDepth();
+ cbD->commands.append(cmd);
+
+ if (!QRHI_RES(QVkGraphicsPipeline, cbD->currentGraphicsPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)) {
+ cmd.cmd = QVkCommandBuffer::Command::SetScissor;
+ VkRect2D *s = &cmd.args.setScissor.scissor;
+ s->offset.x = x;
+ s->offset.y = y;
+ s->extent.width = w;
+ s->extent.height = h;
+ cbD->commands.append(cmd);
+ }
+}
+
+void QRhiVulkan::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor)
+{
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
+ Q_ASSERT(QRHI_RES(QVkGraphicsPipeline, cbD->currentGraphicsPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor));
+ const QSize outputSize = cbD->currentTarget->pixelSize();
+
+ // x,y is top-left in VkRect2D but bottom-left in QRhiScissor
+ int x, y, w, h;
+ if (!qrhi_toTopLeftRenderTargetRect(outputSize, scissor.scissor(), &x, &y, &w, &h))
+ return;
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::SetScissor;
+ VkRect2D *s = &cmd.args.setScissor.scissor;
+ s->offset.x = x;
+ s->offset.y = y;
+ s->extent.width = w;
+ s->extent.height = h;
+ cbD->commands.append(cmd);
+}
+
+void QRhiVulkan::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
+{
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::SetBlendConstants;
+ cmd.args.setBlendConstants.c[0] = c.redF();
+ cmd.args.setBlendConstants.c[1] = c.greenF();
+ cmd.args.setBlendConstants.c[2] = c.blueF();
+ cmd.args.setBlendConstants.c[3] = c.alphaF();
+ cbD->commands.append(cmd);
+}
+
+void QRhiVulkan::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
+{
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::SetStencilRef;
+ cmd.args.setStencilRef.ref = refValue;
+ cbD->commands.append(cmd);
+}
+
+void QRhiVulkan::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
+ quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
+{
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::Draw;
+ cmd.args.draw.vertexCount = vertexCount;
+ cmd.args.draw.instanceCount = instanceCount;
+ cmd.args.draw.firstVertex = firstVertex;
+ cmd.args.draw.firstInstance = firstInstance;
+ cbD->commands.append(cmd);
+}
+
+void QRhiVulkan::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
+ quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
+{
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
+
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::DrawIndexed;
+ cmd.args.drawIndexed.indexCount = indexCount;
+ cmd.args.drawIndexed.instanceCount = instanceCount;
+ cmd.args.drawIndexed.firstIndex = firstIndex;
+ cmd.args.drawIndexed.vertexOffset = vertexOffset;
+ cmd.args.drawIndexed.firstInstance = firstInstance;
+ cbD->commands.append(cmd);
+}
+
+void QRhiVulkan::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
+{
+ if (!debugMarkers || !debugMarkersAvailable)
+ return;
+
+ VkDebugMarkerMarkerInfoEXT marker;
+ memset(&marker, 0, sizeof(marker));
+ marker.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT;
+
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::DebugMarkerBegin;
+ cmd.args.debugMarkerBegin.marker = marker;
+ cmd.args.debugMarkerBegin.markerNameIndex = cbD->pools.debugMarkerName.count();
+ cbD->pools.debugMarkerName.append(name);
+ cbD->commands.append(cmd);
+}
+
+void QRhiVulkan::debugMarkEnd(QRhiCommandBuffer *cb)
+{
+ if (!debugMarkers || !debugMarkersAvailable)
+ return;
+
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::DebugMarkerEnd;
+ cbD->commands.append(cmd);
+}
+
+void QRhiVulkan::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
+{
+ if (!debugMarkers || !debugMarkersAvailable)
+ return;
+
+ VkDebugMarkerMarkerInfoEXT marker;
+ memset(&marker, 0, sizeof(marker));
+ marker.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT;
+ marker.pMarkerName = msg.constData();
+
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ QVkCommandBuffer::Command cmd;
+ cmd.cmd = QVkCommandBuffer::Command::DebugMarkerInsert;
+ cmd.args.debugMarkerInsert.marker = marker;
+ cbD->commands.append(cmd);
+}
+
+const QRhiNativeHandles *QRhiVulkan::nativeHandles(QRhiCommandBuffer *cb)
+{
+ return QRHI_RES(QVkCommandBuffer, cb)->nativeHandles();
+}
+
+void QRhiVulkan::beginExternal(QRhiCommandBuffer *cb)
+{
+ Q_UNUSED(cb);
+}
+
+void QRhiVulkan::endExternal(QRhiCommandBuffer *cb)
+{
+ QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
+ cbD->resetCachedState();
+}
+
+void QRhiVulkan::setObjectName(uint64_t object, VkDebugReportObjectTypeEXT type, const QByteArray &name, int slot)
+{
+ if (!debugMarkers || !debugMarkersAvailable || name.isEmpty())
+ return;
+
+ VkDebugMarkerObjectNameInfoEXT nameInfo;
+ memset(&nameInfo, 0, sizeof(nameInfo));
+ nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT;
+ nameInfo.objectType = type;
+ nameInfo.object = object;
+ QByteArray decoratedName = name;
+ if (slot >= 0) {
+ decoratedName += '/';
+ decoratedName += QByteArray::number(slot);
+ }
+ nameInfo.pObjectName = decoratedName.constData();
+ vkDebugMarkerSetObjectName(dev, &nameInfo);
+}
+
+static inline VkBufferUsageFlagBits toVkBufferUsage(QRhiBuffer::UsageFlags usage)
+{
+ int u = 0;
+ if (usage.testFlag(QRhiBuffer::VertexBuffer))
+ u |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
+ if (usage.testFlag(QRhiBuffer::IndexBuffer))
+ u |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
+ if (usage.testFlag(QRhiBuffer::UniformBuffer))
+ u |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
+ if (usage.testFlag(QRhiBuffer::StorageBuffer))
+ u |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
+ return VkBufferUsageFlagBits(u);
+}
+
+static inline VkFilter toVkFilter(QRhiSampler::Filter f)
+{
+ switch (f) {
+ case QRhiSampler::Nearest:
+ return VK_FILTER_NEAREST;
+ case QRhiSampler::Linear:
+ return VK_FILTER_LINEAR;
+ default:
+ Q_UNREACHABLE();
+ return VK_FILTER_NEAREST;
+ }
+}
+
+static inline VkSamplerMipmapMode toVkMipmapMode(QRhiSampler::Filter f)
+{
+ switch (f) {
+ case QRhiSampler::None:
+ return VK_SAMPLER_MIPMAP_MODE_NEAREST;
+ case QRhiSampler::Nearest:
+ return VK_SAMPLER_MIPMAP_MODE_NEAREST;
+ case QRhiSampler::Linear:
+ return VK_SAMPLER_MIPMAP_MODE_LINEAR;
+ default:
+ Q_UNREACHABLE();
+ return VK_SAMPLER_MIPMAP_MODE_NEAREST;
+ }
+}
+
+static inline VkSamplerAddressMode toVkAddressMode(QRhiSampler::AddressMode m)
+{
+ switch (m) {
+ case QRhiSampler::Repeat:
+ return VK_SAMPLER_ADDRESS_MODE_REPEAT;
+ case QRhiSampler::ClampToEdge:
+ return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ case QRhiSampler::Border:
+ return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
+ case QRhiSampler::Mirror:
+ return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
+ case QRhiSampler::MirrorOnce:
+ return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
+ default:
+ Q_UNREACHABLE();
+ return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ }
+}
+
+static inline VkShaderStageFlagBits toVkShaderStage(QRhiShaderStage::Type type)
+{
+ switch (type) {
+ case QRhiShaderStage::Vertex:
+ return VK_SHADER_STAGE_VERTEX_BIT;
+ case QRhiShaderStage::Fragment:
+ return VK_SHADER_STAGE_FRAGMENT_BIT;
+ case QRhiShaderStage::Compute:
+ return VK_SHADER_STAGE_COMPUTE_BIT;
+ default:
+ Q_UNREACHABLE();
+ return VK_SHADER_STAGE_VERTEX_BIT;
+ }
+}
+
+static inline VkFormat toVkAttributeFormat(QRhiVertexInputAttribute::Format format)
+{
+ switch (format) {
+ case QRhiVertexInputAttribute::Float4:
+ return VK_FORMAT_R32G32B32A32_SFLOAT;
+ case QRhiVertexInputAttribute::Float3:
+ return VK_FORMAT_R32G32B32_SFLOAT;
+ case QRhiVertexInputAttribute::Float2:
+ return VK_FORMAT_R32G32_SFLOAT;
+ case QRhiVertexInputAttribute::Float:
+ return VK_FORMAT_R32_SFLOAT;
+ case QRhiVertexInputAttribute::UNormByte4:
+ return VK_FORMAT_R8G8B8A8_UNORM;
+ case QRhiVertexInputAttribute::UNormByte2:
+ return VK_FORMAT_R8G8_UNORM;
+ case QRhiVertexInputAttribute::UNormByte:
+ return VK_FORMAT_R8_UNORM;
+ default:
+ Q_UNREACHABLE();
+ return VK_FORMAT_R32G32B32A32_SFLOAT;
+ }
+}
+
+static inline VkPrimitiveTopology toVkTopology(QRhiGraphicsPipeline::Topology t)
+{
+ switch (t) {
+ case QRhiGraphicsPipeline::Triangles:
+ return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+ case QRhiGraphicsPipeline::TriangleStrip:
+ return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
+ case QRhiGraphicsPipeline::Lines:
+ return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
+ case QRhiGraphicsPipeline::LineStrip:
+ return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
+ case QRhiGraphicsPipeline::Points:
+ return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
+ default:
+ Q_UNREACHABLE();
+ return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+ }
+}
+
+static inline VkCullModeFlags toVkCullMode(QRhiGraphicsPipeline::CullMode c)
+{
+ switch (c) {
+ case QRhiGraphicsPipeline::None:
+ return VK_CULL_MODE_NONE;
+ case QRhiGraphicsPipeline::Front:
+ return VK_CULL_MODE_FRONT_BIT;
+ case QRhiGraphicsPipeline::Back:
+ return VK_CULL_MODE_BACK_BIT;
+ default:
+ Q_UNREACHABLE();
+ return VK_CULL_MODE_NONE;
+ }
+}
+
+static inline VkFrontFace toVkFrontFace(QRhiGraphicsPipeline::FrontFace f)
+{
+ switch (f) {
+ case QRhiGraphicsPipeline::CCW:
+ return VK_FRONT_FACE_COUNTER_CLOCKWISE;
+ case QRhiGraphicsPipeline::CW:
+ return VK_FRONT_FACE_CLOCKWISE;
+ default:
+ Q_UNREACHABLE();
+ return VK_FRONT_FACE_COUNTER_CLOCKWISE;
+ }
+}
+
+static inline VkColorComponentFlags toVkColorComponents(QRhiGraphicsPipeline::ColorMask c)
+{
+ int f = 0;
+ if (c.testFlag(QRhiGraphicsPipeline::R))
+ f |= VK_COLOR_COMPONENT_R_BIT;
+ if (c.testFlag(QRhiGraphicsPipeline::G))
+ f |= VK_COLOR_COMPONENT_G_BIT;
+ if (c.testFlag(QRhiGraphicsPipeline::B))
+ f |= VK_COLOR_COMPONENT_B_BIT;
+ if (c.testFlag(QRhiGraphicsPipeline::A))
+ f |= VK_COLOR_COMPONENT_A_BIT;
+ return VkColorComponentFlags(f);
+}
+
+static inline VkBlendFactor toVkBlendFactor(QRhiGraphicsPipeline::BlendFactor f)
+{
+ switch (f) {
+ case QRhiGraphicsPipeline::Zero:
+ return VK_BLEND_FACTOR_ZERO;
+ case QRhiGraphicsPipeline::One:
+ return VK_BLEND_FACTOR_ONE;
+ case QRhiGraphicsPipeline::SrcColor:
+ return VK_BLEND_FACTOR_SRC_COLOR;
+ case QRhiGraphicsPipeline::OneMinusSrcColor:
+ return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
+ case QRhiGraphicsPipeline::DstColor:
+ return VK_BLEND_FACTOR_DST_COLOR;
+ case QRhiGraphicsPipeline::OneMinusDstColor:
+ return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
+ case QRhiGraphicsPipeline::SrcAlpha:
+ return VK_BLEND_FACTOR_SRC_ALPHA;
+ case QRhiGraphicsPipeline::OneMinusSrcAlpha:
+ return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+ case QRhiGraphicsPipeline::DstAlpha:
+ return VK_BLEND_FACTOR_DST_ALPHA;
+ case QRhiGraphicsPipeline::OneMinusDstAlpha:
+ return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
+ case QRhiGraphicsPipeline::ConstantColor:
+ return VK_BLEND_FACTOR_CONSTANT_COLOR;
+ case QRhiGraphicsPipeline::OneMinusConstantColor:
+ return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR;
+ case QRhiGraphicsPipeline::ConstantAlpha:
+ return VK_BLEND_FACTOR_CONSTANT_ALPHA;
+ case QRhiGraphicsPipeline::OneMinusConstantAlpha:
+ return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA;
+ case QRhiGraphicsPipeline::SrcAlphaSaturate:
+ return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE;
+ case QRhiGraphicsPipeline::Src1Color:
+ return VK_BLEND_FACTOR_SRC1_COLOR;
+ case QRhiGraphicsPipeline::OneMinusSrc1Color:
+ return VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR;
+ case QRhiGraphicsPipeline::Src1Alpha:
+ return VK_BLEND_FACTOR_SRC1_ALPHA;
+ case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
+ return VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA;
+ default:
+ Q_UNREACHABLE();
+ return VK_BLEND_FACTOR_ZERO;
+ }
+}
+
+static inline VkBlendOp toVkBlendOp(QRhiGraphicsPipeline::BlendOp op)
+{
+ switch (op) {
+ case QRhiGraphicsPipeline::Add:
+ return VK_BLEND_OP_ADD;
+ case QRhiGraphicsPipeline::Subtract:
+ return VK_BLEND_OP_SUBTRACT;
+ case QRhiGraphicsPipeline::ReverseSubtract:
+ return VK_BLEND_OP_REVERSE_SUBTRACT;
+ case QRhiGraphicsPipeline::Min:
+ return VK_BLEND_OP_MIN;
+ case QRhiGraphicsPipeline::Max:
+ return VK_BLEND_OP_MAX;
+ default:
+ Q_UNREACHABLE();
+ return VK_BLEND_OP_ADD;
+ }
+}
+
+static inline VkCompareOp toVkCompareOp(QRhiGraphicsPipeline::CompareOp op)
+{
+ switch (op) {
+ case QRhiGraphicsPipeline::Never:
+ return VK_COMPARE_OP_NEVER;
+ case QRhiGraphicsPipeline::Less:
+ return VK_COMPARE_OP_LESS;
+ case QRhiGraphicsPipeline::Equal:
+ return VK_COMPARE_OP_EQUAL;
+ case QRhiGraphicsPipeline::LessOrEqual:
+ return VK_COMPARE_OP_LESS_OR_EQUAL;
+ case QRhiGraphicsPipeline::Greater:
+ return VK_COMPARE_OP_GREATER;
+ case QRhiGraphicsPipeline::NotEqual:
+ return VK_COMPARE_OP_NOT_EQUAL;
+ case QRhiGraphicsPipeline::GreaterOrEqual:
+ return VK_COMPARE_OP_GREATER_OR_EQUAL;
+ case QRhiGraphicsPipeline::Always:
+ return VK_COMPARE_OP_ALWAYS;
+ default:
+ Q_UNREACHABLE();
+ return VK_COMPARE_OP_ALWAYS;
+ }
+}
+
+static inline VkStencilOp toVkStencilOp(QRhiGraphicsPipeline::StencilOp op)
+{
+ switch (op) {
+ case QRhiGraphicsPipeline::StencilZero:
+ return VK_STENCIL_OP_ZERO;
+ case QRhiGraphicsPipeline::Keep:
+ return VK_STENCIL_OP_KEEP;
+ case QRhiGraphicsPipeline::Replace:
+ return VK_STENCIL_OP_REPLACE;
+ case QRhiGraphicsPipeline::IncrementAndClamp:
+ return VK_STENCIL_OP_INCREMENT_AND_CLAMP;
+ case QRhiGraphicsPipeline::DecrementAndClamp:
+ return VK_STENCIL_OP_DECREMENT_AND_CLAMP;
+ case QRhiGraphicsPipeline::Invert:
+ return VK_STENCIL_OP_INVERT;
+ case QRhiGraphicsPipeline::IncrementAndWrap:
+ return VK_STENCIL_OP_INCREMENT_AND_WRAP;
+ case QRhiGraphicsPipeline::DecrementAndWrap:
+ return VK_STENCIL_OP_DECREMENT_AND_WRAP;
+ default:
+ Q_UNREACHABLE();
+ return VK_STENCIL_OP_KEEP;
+ }
+}
+
+static inline void fillVkStencilOpState(VkStencilOpState *dst, const QRhiGraphicsPipeline::StencilOpState &src)
+{
+ dst->failOp = toVkStencilOp(src.failOp);
+ dst->passOp = toVkStencilOp(src.passOp);
+ dst->depthFailOp = toVkStencilOp(src.depthFailOp);
+ dst->compareOp = toVkCompareOp(src.compareOp);
+}
+
+static inline VkDescriptorType toVkDescriptorType(const QRhiShaderResourceBindingPrivate *b)
+{
+ switch (b->type) {
+ case QRhiShaderResourceBinding::UniformBuffer:
+ return b->u.ubuf.hasDynamicOffset ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC
+ : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+
+ case QRhiShaderResourceBinding::SampledTexture:
+ return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+
+ case QRhiShaderResourceBinding::ImageLoad:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::ImageStore:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::ImageLoadStore:
+ return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
+
+ case QRhiShaderResourceBinding::BufferLoad:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::BufferStore:
+ Q_FALLTHROUGH();
+ case QRhiShaderResourceBinding::BufferLoadStore:
+ return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+
+ default:
+ Q_UNREACHABLE();
+ return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ }
+}
+
+static inline VkShaderStageFlags toVkShaderStageFlags(QRhiShaderResourceBinding::StageFlags stage)
+{
+ int s = 0;
+ if (stage.testFlag(QRhiShaderResourceBinding::VertexStage))
+ s |= VK_SHADER_STAGE_VERTEX_BIT;
+ if (stage.testFlag(QRhiShaderResourceBinding::FragmentStage))
+ s |= VK_SHADER_STAGE_FRAGMENT_BIT;
+ if (stage.testFlag(QRhiShaderResourceBinding::ComputeStage))
+ s |= VK_SHADER_STAGE_COMPUTE_BIT;
+ return VkShaderStageFlags(s);
+}
+
+static inline VkCompareOp toVkTextureCompareOp(QRhiSampler::CompareOp op)
+{
+ switch (op) {
+ case QRhiSampler::Never:
+ return VK_COMPARE_OP_NEVER;
+ case QRhiSampler::Less:
+ return VK_COMPARE_OP_LESS;
+ case QRhiSampler::Equal:
+ return VK_COMPARE_OP_EQUAL;
+ case QRhiSampler::LessOrEqual:
+ return VK_COMPARE_OP_LESS_OR_EQUAL;
+ case QRhiSampler::Greater:
+ return VK_COMPARE_OP_GREATER;
+ case QRhiSampler::NotEqual:
+ return VK_COMPARE_OP_NOT_EQUAL;
+ case QRhiSampler::GreaterOrEqual:
+ return VK_COMPARE_OP_GREATER_OR_EQUAL;
+ case QRhiSampler::Always:
+ return VK_COMPARE_OP_ALWAYS;
+ default:
+ Q_UNREACHABLE();
+ return VK_COMPARE_OP_NEVER;
+ }
+}
+
+QVkBuffer::QVkBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size)
+ : QRhiBuffer(rhi, type, usage, size)
+{
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
+ buffers[i] = stagingBuffers[i] = VK_NULL_HANDLE;
+ allocations[i] = stagingAllocations[i] = nullptr;
+ }
+}
+
+QVkBuffer::~QVkBuffer()
+{
+ release();
+}
+
+void QVkBuffer::release()
+{
+ if (!buffers[0])
+ return;
+
+ QRhiVulkan::DeferredReleaseEntry e;
+ e.type = QRhiVulkan::DeferredReleaseEntry::Buffer;
+ e.lastActiveFrameSlot = lastActiveFrameSlot;
+
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
+ e.buffer.buffers[i] = buffers[i];
+ e.buffer.allocations[i] = allocations[i];
+ e.buffer.stagingBuffers[i] = stagingBuffers[i];
+ e.buffer.stagingAllocations[i] = stagingAllocations[i];
+
+ buffers[i] = VK_NULL_HANDLE;
+ allocations[i] = nullptr;
+ stagingBuffers[i] = VK_NULL_HANDLE;
+ stagingAllocations[i] = nullptr;
+ pendingDynamicUpdates[i].clear();
+ }
+
+ QRHI_RES_RHI(QRhiVulkan);
+ rhiD->releaseQueue.append(e);
+
+ QRHI_PROF;
+ QRHI_PROF_F(releaseBuffer(this));
+
+ rhiD->unregisterResource(this);
+}
+
+bool QVkBuffer::build()
+{
+ if (buffers[0])
+ release();
+
+ if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) {
+ qWarning("StorageBuffer cannot be combined with Dynamic");
+ return false;
+ }
+
+ const int nonZeroSize = m_size <= 0 ? 256 : m_size;
+
+ VkBufferCreateInfo bufferInfo;
+ memset(&bufferInfo, 0, sizeof(bufferInfo));
+ bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ bufferInfo.size = nonZeroSize;
+ bufferInfo.usage = toVkBufferUsage(m_usage);
+
+ VmaAllocationCreateInfo allocInfo;
+ memset(&allocInfo, 0, sizeof(allocInfo));
+
+ if (m_type == Dynamic) {
+#ifndef Q_OS_DARWIN // not for MoltenVK
+ // Keep mapped all the time. Essential f.ex. with some mobile GPUs,
+ // where mapping and unmapping an entire allocation every time updating
+ // a suballocated buffer presents a significant perf. hit.
+ allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
+#endif
+ // host visible, frequent changes
+ allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
+ } else {
+ allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
+ bufferInfo.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
+ }
+
+ QRHI_RES_RHI(QRhiVulkan);
+ VkResult err = VK_SUCCESS;
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
+ buffers[i] = VK_NULL_HANDLE;
+ allocations[i] = nullptr;
+ usageState[i].access = usageState[i].stage = 0;
+ if (i == 0 || m_type == Dynamic) {
+ VmaAllocation allocation;
+ err = vmaCreateBuffer(toVmaAllocator(rhiD->allocator), &bufferInfo, &allocInfo, &buffers[i], &allocation, nullptr);
+ if (err != VK_SUCCESS)
+ break;
+
+ allocations[i] = allocation;
+ if (m_type == Dynamic)
+ pendingDynamicUpdates[i].reserve(16);
+
+ rhiD->setObjectName(uint64_t(buffers[i]), VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT, m_objectName,
+ m_type == Dynamic ? i : -1);
+ }
+ }
+
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create buffer: %d", err);
+ return false;
+ }
+
+ QRHI_PROF;
+ QRHI_PROF_F(newBuffer(this, nonZeroSize, m_type != Dynamic ? 1 : QVK_FRAMES_IN_FLIGHT, 0));
+
+ lastActiveFrameSlot = -1;
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+QVkRenderBuffer::QVkRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
+ int sampleCount, Flags flags)
+ : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags)
+{
+}
+
+QVkRenderBuffer::~QVkRenderBuffer()
+{
+ release();
+ delete backingTexture;
+}
+
+void QVkRenderBuffer::release()
+{
+ if (!memory && !backingTexture)
+ return;
+
+ QRhiVulkan::DeferredReleaseEntry e;
+ e.type = QRhiVulkan::DeferredReleaseEntry::RenderBuffer;
+ e.lastActiveFrameSlot = lastActiveFrameSlot;
+
+ e.renderBuffer.memory = memory;
+ e.renderBuffer.image = image;
+ e.renderBuffer.imageView = imageView;
+
+ memory = VK_NULL_HANDLE;
+ image = VK_NULL_HANDLE;
+ imageView = VK_NULL_HANDLE;
+
+ if (backingTexture) {
+ Q_ASSERT(backingTexture->lastActiveFrameSlot == -1);
+ backingTexture->lastActiveFrameSlot = e.lastActiveFrameSlot;
+ backingTexture->release();
+ }
+
+ QRHI_RES_RHI(QRhiVulkan);
+ rhiD->releaseQueue.append(e);
+
+ QRHI_PROF;
+ QRHI_PROF_F(releaseRenderBuffer(this));
+
+ rhiD->unregisterResource(this);
+}
+
+bool QVkRenderBuffer::build()
+{
+ if (memory || backingTexture)
+ release();
+
+ if (m_pixelSize.isEmpty())
+ return false;
+
+ QRHI_RES_RHI(QRhiVulkan);
+ QRHI_PROF;
+ samples = rhiD->effectiveSampleCount(m_sampleCount);
+
+ switch (m_type) {
+ case QRhiRenderBuffer::Color:
+ {
+ if (!backingTexture) {
+ backingTexture = QRHI_RES(QVkTexture, rhiD->createTexture(QRhiTexture::RGBA8,
+ m_pixelSize,
+ m_sampleCount,
+ QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
+ } else {
+ backingTexture->setPixelSize(m_pixelSize);
+ backingTexture->setSampleCount(m_sampleCount);
+ }
+ backingTexture->setName(m_objectName);
+ if (!backingTexture->build())
+ return false;
+ vkformat = backingTexture->vkformat;
+ QRHI_PROF_F(newRenderBuffer(this, false, false, samples));
+ }
+ break;
+ case QRhiRenderBuffer::DepthStencil:
+ vkformat = rhiD->optimalDepthStencilFormat();
+ if (!rhiD->createTransientImage(vkformat,
+ m_pixelSize,
+ VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
+ VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
+ samples,
+ &memory,
+ &image,
+ &imageView,
+ 1))
+ {
+ return false;
+ }
+ rhiD->setObjectName(uint64_t(image), VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, m_objectName);
+ QRHI_PROF_F(newRenderBuffer(this, true, false, samples));
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ lastActiveFrameSlot = -1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+QRhiTexture::Format QVkRenderBuffer::backingFormat() const
+{
+ return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
+}
+
+QVkTexture::QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+ int sampleCount, Flags flags)
+ : QRhiTexture(rhi, format, pixelSize, sampleCount, flags)
+{
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
+ stagingBuffers[i] = VK_NULL_HANDLE;
+ stagingAllocations[i] = nullptr;
+ }
+ for (int i = 0; i < QRhi::MAX_LEVELS; ++i)
+ perLevelImageViews[i] = VK_NULL_HANDLE;
+}
+
+QVkTexture::~QVkTexture()
+{
+ release();
+}
+
+void QVkTexture::release()
+{
+ if (!image)
+ return;
+
+ QRhiVulkan::DeferredReleaseEntry e;
+ e.type = QRhiVulkan::DeferredReleaseEntry::Texture;
+ e.lastActiveFrameSlot = lastActiveFrameSlot;
+
+ e.texture.image = owns ? image : VK_NULL_HANDLE;
+ e.texture.imageView = imageView;
+ e.texture.allocation = owns ? imageAlloc : nullptr;
+
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
+ e.texture.stagingBuffers[i] = stagingBuffers[i];
+ e.texture.stagingAllocations[i] = stagingAllocations[i];
+
+ stagingBuffers[i] = VK_NULL_HANDLE;
+ stagingAllocations[i] = nullptr;
+ }
+
+ for (int i = 0; i < QRhi::MAX_LEVELS; ++i) {
+ e.texture.extraImageViews[i] = perLevelImageViews[i];
+ perLevelImageViews[i] = VK_NULL_HANDLE;
+ }
+
+ image = VK_NULL_HANDLE;
+ imageView = VK_NULL_HANDLE;
+ imageAlloc = nullptr;
+ nativeHandlesStruct.image = VK_NULL_HANDLE;
+
+ QRHI_RES_RHI(QRhiVulkan);
+ rhiD->releaseQueue.append(e);
+
+ QRHI_PROF;
+ QRHI_PROF_F(releaseTexture(this));
+
+ rhiD->unregisterResource(this);
+}
+
+bool QVkTexture::prepareBuild(QSize *adjustedSize)
+{
+ if (image)
+ release();
+
+ QRHI_RES_RHI(QRhiVulkan);
+ vkformat = toVkTextureFormat(m_format, m_flags);
+ VkFormatProperties props;
+ rhiD->f->vkGetPhysicalDeviceFormatProperties(rhiD->physDev, vkformat, &props);
+ const bool canSampleOptimal = (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
+ if (!canSampleOptimal) {
+ qWarning("Texture sampling with optimal tiling for format %d not supported", vkformat);
+ return false;
+ }
+
+ const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
+ const bool isCube = m_flags.testFlag(CubeMap);
+ const bool hasMipMaps = m_flags.testFlag(MipMapped);
+
+ mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
+ const int maxLevels = QRhi::MAX_LEVELS;
+ if (mipLevelCount > maxLevels) {
+ qWarning("Too many mip levels (%d, max is %d), truncating mip chain", mipLevelCount, maxLevels);
+ mipLevelCount = maxLevels;
+ }
+ samples = rhiD->effectiveSampleCount(m_sampleCount);
+ if (samples > VK_SAMPLE_COUNT_1_BIT) {
+ if (isCube) {
+ qWarning("Cubemap texture cannot be multisample");
+ return false;
+ }
+ if (hasMipMaps) {
+ qWarning("Multisample texture cannot have mipmaps");
+ return false;
+ }
+ }
+
+ usageState.layout = VK_IMAGE_LAYOUT_PREINITIALIZED;
+ usageState.access = 0;
+ usageState.stage = 0;
+
+ if (adjustedSize)
+ *adjustedSize = size;
+
+ return true;
+}
+
+bool QVkTexture::finishBuild()
+{
+ QRHI_RES_RHI(QRhiVulkan);
+
+ const bool isDepth = isDepthTextureFormat(m_format);
+ const bool isCube = m_flags.testFlag(CubeMap);
+
+ VkImageViewCreateInfo viewInfo;
+ memset(&viewInfo, 0, sizeof(viewInfo));
+ viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ viewInfo.image = image;
+ viewInfo.viewType = isCube ? VK_IMAGE_VIEW_TYPE_CUBE : VK_IMAGE_VIEW_TYPE_2D;
+ viewInfo.format = vkformat;
+ viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
+ viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
+ viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
+ viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
+ viewInfo.subresourceRange.aspectMask = isDepth ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
+ viewInfo.subresourceRange.levelCount = mipLevelCount;
+ viewInfo.subresourceRange.layerCount = isCube ? 6 : 1;
+
+ VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &imageView);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create image view: %d", err);
+ return false;
+ }
+
+ nativeHandlesStruct.image = image;
+
+ lastActiveFrameSlot = -1;
+ generation += 1;
+
+ return true;
+}
+
+bool QVkTexture::build()
+{
+ QSize size;
+ if (!prepareBuild(&size))
+ return false;
+
+ const bool isRenderTarget = m_flags.testFlag(QRhiTexture::RenderTarget);
+ const bool isDepth = isDepthTextureFormat(m_format);
+ const bool isCube = m_flags.testFlag(CubeMap);
+
+ VkImageCreateInfo imageInfo;
+ memset(&imageInfo, 0, sizeof(imageInfo));
+ imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+ imageInfo.flags = isCube ? VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT : 0;
+ imageInfo.imageType = VK_IMAGE_TYPE_2D;
+ imageInfo.format = vkformat;
+ imageInfo.extent.width = size.width();
+ imageInfo.extent.height = size.height();
+ imageInfo.extent.depth = 1;
+ imageInfo.mipLevels = mipLevelCount;
+ imageInfo.arrayLayers = isCube ? 6 : 1;
+ imageInfo.samples = samples;
+ imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
+ imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
+
+ imageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+ if (isRenderTarget) {
+ if (isDepth)
+ imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+ else
+ imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+ }
+ if (m_flags.testFlag(QRhiTexture::UsedAsTransferSource))
+ imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
+ if (m_flags.testFlag(QRhiTexture::UsedWithGenerateMips))
+ imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
+ if (m_flags.testFlag(QRhiTexture::UsedWithLoadStore))
+ imageInfo.usage |= VK_IMAGE_USAGE_STORAGE_BIT;
+
+ VmaAllocationCreateInfo allocInfo;
+ memset(&allocInfo, 0, sizeof(allocInfo));
+ allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
+
+ QRHI_RES_RHI(QRhiVulkan);
+ VmaAllocation allocation;
+ VkResult err = vmaCreateImage(toVmaAllocator(rhiD->allocator), &imageInfo, &allocInfo, &image, &allocation, nullptr);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create image: %d", err);
+ return false;
+ }
+ imageAlloc = allocation;
+
+ if (!finishBuild())
+ return false;
+
+ rhiD->setObjectName(uint64_t(image), VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, m_objectName);
+
+ QRHI_PROF;
+ QRHI_PROF_F(newTexture(this, true, mipLevelCount, isCube ? 6 : 1, samples));
+
+ owns = true;
+ rhiD->registerResource(this);
+ return true;
+}
+
+bool QVkTexture::buildFrom(const QRhiNativeHandles *src)
+{
+ const QRhiVulkanTextureNativeHandles *h = static_cast<const QRhiVulkanTextureNativeHandles *>(src);
+ if (!h || !h->image)
+ return false;
+
+ if (!prepareBuild())
+ return false;
+
+ image = h->image;
+
+ if (!finishBuild())
+ return false;
+
+ QRHI_PROF;
+ QRHI_PROF_F(newTexture(this, false, mipLevelCount, m_flags.testFlag(CubeMap) ? 6 : 1, samples));
+
+ usageState.layout = h->layout;
+
+ owns = false;
+ QRHI_RES_RHI(QRhiVulkan);
+ rhiD->registerResource(this);
+ return true;
+}
+
+const QRhiNativeHandles *QVkTexture::nativeHandles()
+{
+ nativeHandlesStruct.layout = usageState.layout;
+ return &nativeHandlesStruct;
+}
+
+VkImageView QVkTexture::imageViewForLevel(int level)
+{
+ Q_ASSERT(level >= 0 && level < int(mipLevelCount));
+ if (perLevelImageViews[level] != VK_NULL_HANDLE)
+ return perLevelImageViews[level];
+
+ const bool isDepth = isDepthTextureFormat(m_format);
+ const bool isCube = m_flags.testFlag(CubeMap);
+
+ VkImageViewCreateInfo viewInfo;
+ memset(&viewInfo, 0, sizeof(viewInfo));
+ viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ viewInfo.image = image;
+ viewInfo.viewType = isCube ? VK_IMAGE_VIEW_TYPE_CUBE : VK_IMAGE_VIEW_TYPE_2D;
+ viewInfo.format = vkformat;
+ viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
+ viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
+ viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
+ viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
+ viewInfo.subresourceRange.aspectMask = isDepth ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
+ viewInfo.subresourceRange.baseMipLevel = level;
+ viewInfo.subresourceRange.levelCount = 1;
+ viewInfo.subresourceRange.baseArrayLayer = 0;
+ viewInfo.subresourceRange.layerCount = isCube ? 6 : 1;
+
+ VkImageView v = VK_NULL_HANDLE;
+ QRHI_RES_RHI(QRhiVulkan);
+ VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &v);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create image view: %d", err);
+ return VK_NULL_HANDLE;
+ }
+
+ perLevelImageViews[level] = v;
+ return v;
+}
+
+QVkSampler::QVkSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
+ AddressMode u, AddressMode v)
+ : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v)
+{
+}
+
+QVkSampler::~QVkSampler()
+{
+ release();
+}
+
+void QVkSampler::release()
+{
+ if (!sampler)
+ return;
+
+ QRhiVulkan::DeferredReleaseEntry e;
+ e.type = QRhiVulkan::DeferredReleaseEntry::Sampler;
+ e.lastActiveFrameSlot = lastActiveFrameSlot;
+
+ e.sampler.sampler = sampler;
+ sampler = VK_NULL_HANDLE;
+
+ QRHI_RES_RHI(QRhiVulkan);
+ rhiD->releaseQueue.append(e);
+ rhiD->unregisterResource(this);
+}
+
+bool QVkSampler::build()
+{
+ if (sampler)
+ release();
+
+ VkSamplerCreateInfo samplerInfo;
+ memset(&samplerInfo, 0, sizeof(samplerInfo));
+ samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
+ samplerInfo.magFilter = toVkFilter(m_magFilter);
+ samplerInfo.minFilter = toVkFilter(m_minFilter);
+ samplerInfo.mipmapMode = toVkMipmapMode(m_mipmapMode);
+ samplerInfo.addressModeU = toVkAddressMode(m_addressU);
+ samplerInfo.addressModeV = toVkAddressMode(m_addressV);
+ samplerInfo.addressModeW = toVkAddressMode(m_addressW);
+ samplerInfo.maxAnisotropy = 1.0f;
+ samplerInfo.compareEnable = m_compareOp != Never;
+ samplerInfo.compareOp = toVkTextureCompareOp(m_compareOp);
+ samplerInfo.maxLod = m_mipmapMode == None ? 0.25f : 1000.0f;
+
+ QRHI_RES_RHI(QRhiVulkan);
+ VkResult err = rhiD->df->vkCreateSampler(rhiD->dev, &samplerInfo, nullptr, &sampler);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create sampler: %d", err);
+ return false;
+ }
+
+ lastActiveFrameSlot = -1;
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+QVkRenderPassDescriptor::QVkRenderPassDescriptor(QRhiImplementation *rhi)
+ : QRhiRenderPassDescriptor(rhi)
+{
+}
+
+QVkRenderPassDescriptor::~QVkRenderPassDescriptor()
+{
+ release();
+}
+
+void QVkRenderPassDescriptor::release()
+{
+ if (!rp)
+ return;
+
+ if (!ownsRp) {
+ rp = VK_NULL_HANDLE;
+ return;
+ }
+
+ QRhiVulkan::DeferredReleaseEntry e;
+ e.type = QRhiVulkan::DeferredReleaseEntry::RenderPass;
+ e.lastActiveFrameSlot = lastActiveFrameSlot;
+
+ e.renderPass.rp = rp;
+
+ rp = VK_NULL_HANDLE;
+
+ QRHI_RES_RHI(QRhiVulkan);
+ rhiD->releaseQueue.append(e);
+
+ rhiD->unregisterResource(this);
+}
+
+QVkReferenceRenderTarget::QVkReferenceRenderTarget(QRhiImplementation *rhi)
+ : QRhiRenderTarget(rhi)
+{
+}
+
+QVkReferenceRenderTarget::~QVkReferenceRenderTarget()
+{
+ release();
+}
+
+void QVkReferenceRenderTarget::release()
+{
+ // nothing to do here
+}
+
+QSize QVkReferenceRenderTarget::pixelSize() const
+{
+ return d.pixelSize;
+}
+
+float QVkReferenceRenderTarget::devicePixelRatio() const
+{
+ return d.dpr;
+}
+
+int QVkReferenceRenderTarget::sampleCount() const
+{
+ return d.sampleCount;
+}
+
+QVkTextureRenderTarget::QVkTextureRenderTarget(QRhiImplementation *rhi,
+ const QRhiTextureRenderTargetDescription &desc,
+ Flags flags)
+ : QRhiTextureRenderTarget(rhi, desc, flags)
+{
+ for (int att = 0; att < QVkRenderTargetData::MAX_COLOR_ATTACHMENTS; ++att) {
+ rtv[att] = VK_NULL_HANDLE;
+ resrtv[att] = VK_NULL_HANDLE;
+ }
+}
+
+QVkTextureRenderTarget::~QVkTextureRenderTarget()
+{
+ release();
+}
+
+void QVkTextureRenderTarget::release()
+{
+ if (!d.fb)
+ return;
+
+ QRhiVulkan::DeferredReleaseEntry e;
+ e.type = QRhiVulkan::DeferredReleaseEntry::TextureRenderTarget;
+ e.lastActiveFrameSlot = lastActiveFrameSlot;
+
+ e.textureRenderTarget.fb = d.fb;
+ d.fb = VK_NULL_HANDLE;
+
+ for (int att = 0; att < QVkRenderTargetData::MAX_COLOR_ATTACHMENTS; ++att) {
+ e.textureRenderTarget.rtv[att] = rtv[att];
+ e.textureRenderTarget.resrtv[att] = resrtv[att];
+ rtv[att] = VK_NULL_HANDLE;
+ resrtv[att] = VK_NULL_HANDLE;
+ }
+
+ QRHI_RES_RHI(QRhiVulkan);
+ rhiD->releaseQueue.append(e);
+
+ rhiD->unregisterResource(this);
+}
+
+QRhiRenderPassDescriptor *QVkTextureRenderTarget::newCompatibleRenderPassDescriptor()
+{
+ // not yet built so cannot rely on data computed in build()
+
+ QRHI_RES_RHI(QRhiVulkan);
+ QVkRenderPassDescriptor *rp = new QVkRenderPassDescriptor(m_rhi);
+ if (!rhiD->createOffscreenRenderPass(&rp->rp,
+ m_desc.colorAttachments(),
+ m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents),
+ m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents),
+ m_desc.depthStencilBuffer(),
+ m_desc.depthTexture()))
+ {
+ delete rp;
+ return nullptr;
+ }
+
+ rp->ownsRp = true;
+ rhiD->registerResource(rp);
+ return rp;
+}
+
+bool QVkTextureRenderTarget::build()
+{
+ if (d.fb)
+ release();
+
+ const QVector<QRhiColorAttachment> colorAttachments = m_desc.colorAttachments();
+ Q_ASSERT(!colorAttachments.isEmpty() || m_desc.depthTexture());
+ Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
+ const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
+
+ QRHI_RES_RHI(QRhiVulkan);
+ QVarLengthArray<VkImageView, 8> views;
+
+ d.colorAttCount = colorAttachments.count();
+ for (int i = 0; i < d.colorAttCount; ++i) {
+ QVkTexture *texD = QRHI_RES(QVkTexture, colorAttachments[i].texture());
+ QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, colorAttachments[i].renderBuffer());
+ Q_ASSERT(texD || rbD);
+ if (texD) {
+ Q_ASSERT(texD->flags().testFlag(QRhiTexture::RenderTarget));
+ VkImageViewCreateInfo viewInfo;
+ memset(&viewInfo, 0, sizeof(viewInfo));
+ viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ viewInfo.image = texD->image;
+ viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ viewInfo.format = texD->vkformat;
+ viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
+ viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
+ viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
+ viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
+ viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ viewInfo.subresourceRange.baseMipLevel = colorAttachments[i].level();
+ viewInfo.subresourceRange.levelCount = 1;
+ viewInfo.subresourceRange.baseArrayLayer = colorAttachments[i].layer();
+ viewInfo.subresourceRange.layerCount = 1;
+ VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &rtv[i]);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create render target image view: %d", err);
+ return false;
+ }
+ views.append(rtv[i]);
+ if (i == 0) {
+ d.pixelSize = texD->pixelSize();
+ d.sampleCount = texD->samples;
+ }
+ } else if (rbD) {
+ Q_ASSERT(rbD->backingTexture);
+ views.append(rbD->backingTexture->imageView);
+ if (i == 0) {
+ d.pixelSize = rbD->pixelSize();
+ d.sampleCount = rbD->samples;
+ }
+ }
+ }
+ d.dpr = 1;
+
+ if (hasDepthStencil) {
+ if (m_desc.depthTexture()) {
+ QVkTexture *depthTexD = QRHI_RES(QVkTexture, m_desc.depthTexture());
+ views.append(depthTexD->imageView);
+ if (d.colorAttCount == 0) {
+ d.pixelSize = depthTexD->pixelSize();
+ d.sampleCount = depthTexD->samples;
+ }
+ } else {
+ QVkRenderBuffer *depthRbD = QRHI_RES(QVkRenderBuffer, m_desc.depthStencilBuffer());
+ views.append(depthRbD->imageView);
+ if (d.colorAttCount == 0) {
+ d.pixelSize = depthRbD->pixelSize();
+ d.sampleCount = depthRbD->samples;
+ }
+ }
+ d.dsAttCount = 1;
+ } else {
+ d.dsAttCount = 0;
+ }
+
+ d.resolveAttCount = 0;
+ for (int i = 0; i < d.colorAttCount; ++i) {
+ if (colorAttachments[i].resolveTexture()) {
+ QVkTexture *resTexD = QRHI_RES(QVkTexture, colorAttachments[i].resolveTexture());
+ Q_ASSERT(resTexD->flags().testFlag(QRhiTexture::RenderTarget));
+ d.resolveAttCount += 1;
+
+ VkImageViewCreateInfo viewInfo;
+ memset(&viewInfo, 0, sizeof(viewInfo));
+ viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ viewInfo.image = resTexD->image;
+ viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ viewInfo.format = resTexD->vkformat;
+ viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
+ viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
+ viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
+ viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
+ viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ viewInfo.subresourceRange.baseMipLevel = colorAttachments[i].resolveLevel();
+ viewInfo.subresourceRange.levelCount = 1;
+ viewInfo.subresourceRange.baseArrayLayer = colorAttachments[i].resolveLayer();
+ viewInfo.subresourceRange.layerCount = 1;
+ VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &resrtv[i]);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create render target resolve image view: %d", err);
+ return false;
+ }
+ views.append(resrtv[i]);
+ }
+ }
+
+ if (!m_renderPassDesc)
+ qWarning("QVkTextureRenderTarget: No renderpass descriptor set. See newCompatibleRenderPassDescriptor() and setRenderPassDescriptor().");
+
+ d.rp = QRHI_RES(QVkRenderPassDescriptor, m_renderPassDesc);
+ Q_ASSERT(d.rp && d.rp->rp);
+
+ VkFramebufferCreateInfo fbInfo;
+ memset(&fbInfo, 0, sizeof(fbInfo));
+ fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+ fbInfo.renderPass = d.rp->rp;
+ fbInfo.attachmentCount = d.colorAttCount + d.dsAttCount + d.resolveAttCount;
+ fbInfo.pAttachments = views.constData();
+ fbInfo.width = d.pixelSize.width();
+ fbInfo.height = d.pixelSize.height();
+ fbInfo.layers = 1;
+
+ VkResult err = rhiD->df->vkCreateFramebuffer(rhiD->dev, &fbInfo, nullptr, &d.fb);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create framebuffer: %d", err);
+ return false;
+ }
+
+ lastActiveFrameSlot = -1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+QSize QVkTextureRenderTarget::pixelSize() const
+{
+ return d.pixelSize;
+}
+
+float QVkTextureRenderTarget::devicePixelRatio() const
+{
+ return d.dpr;
+}
+
+int QVkTextureRenderTarget::sampleCount() const
+{
+ return d.sampleCount;
+}
+
+QVkShaderResourceBindings::QVkShaderResourceBindings(QRhiImplementation *rhi)
+ : QRhiShaderResourceBindings(rhi)
+{
+}
+
+QVkShaderResourceBindings::~QVkShaderResourceBindings()
+{
+ release();
+}
+
+void QVkShaderResourceBindings::release()
+{
+ if (!layout)
+ return;
+
+ sortedBindings.clear();
+
+ QRhiVulkan::DeferredReleaseEntry e;
+ e.type = QRhiVulkan::DeferredReleaseEntry::ShaderResourceBindings;
+ e.lastActiveFrameSlot = lastActiveFrameSlot;
+
+ e.shaderResourceBindings.poolIndex = poolIndex;
+ e.shaderResourceBindings.layout = layout;
+
+ poolIndex = -1;
+ layout = VK_NULL_HANDLE;
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
+ descSets[i] = VK_NULL_HANDLE;
+
+ QRHI_RES_RHI(QRhiVulkan);
+ rhiD->releaseQueue.append(e);
+
+ rhiD->unregisterResource(this);
+}
+
+bool QVkShaderResourceBindings::build()
+{
+ if (layout)
+ release();
+
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
+ descSets[i] = VK_NULL_HANDLE;
+
+ sortedBindings = m_bindings;
+ std::sort(sortedBindings.begin(), sortedBindings.end(),
+ [](const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b)
+ {
+ return QRhiShaderResourceBindingPrivate::get(&a)->binding < QRhiShaderResourceBindingPrivate::get(&b)->binding;
+ });
+
+ QVarLengthArray<VkDescriptorSetLayoutBinding, 4> vkbindings;
+ for (const QRhiShaderResourceBinding &binding : qAsConst(sortedBindings)) {
+ const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&binding);
+ VkDescriptorSetLayoutBinding vkbinding;
+ memset(&vkbinding, 0, sizeof(vkbinding));
+ vkbinding.binding = b->binding;
+ vkbinding.descriptorType = toVkDescriptorType(b);
+ vkbinding.descriptorCount = 1; // no array support yet
+ vkbinding.stageFlags = toVkShaderStageFlags(b->stage);
+ vkbindings.append(vkbinding);
+ }
+
+ VkDescriptorSetLayoutCreateInfo layoutInfo;
+ memset(&layoutInfo, 0, sizeof(layoutInfo));
+ layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+ layoutInfo.bindingCount = uint32_t(vkbindings.count());
+ layoutInfo.pBindings = vkbindings.constData();
+
+ QRHI_RES_RHI(QRhiVulkan);
+ VkResult err = rhiD->df->vkCreateDescriptorSetLayout(rhiD->dev, &layoutInfo, nullptr, &layout);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create descriptor set layout: %d", err);
+ return false;
+ }
+
+ VkDescriptorSetAllocateInfo allocInfo;
+ memset(&allocInfo, 0, sizeof(allocInfo));
+ allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+ allocInfo.descriptorSetCount = QVK_FRAMES_IN_FLIGHT;
+ VkDescriptorSetLayout layouts[QVK_FRAMES_IN_FLIGHT];
+ for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
+ layouts[i] = layout;
+ allocInfo.pSetLayouts = layouts;
+ if (!rhiD->allocateDescriptorSet(&allocInfo, descSets, &poolIndex))
+ return false;
+
+ rhiD->updateShaderResourceBindings(this);
+
+ lastActiveFrameSlot = -1;
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+QVkGraphicsPipeline::QVkGraphicsPipeline(QRhiImplementation *rhi)
+ : QRhiGraphicsPipeline(rhi)
+{
+}
+
+QVkGraphicsPipeline::~QVkGraphicsPipeline()
+{
+ release();
+}
+
+void QVkGraphicsPipeline::release()
+{
+ if (!pipeline && !layout)
+ return;
+
+ QRhiVulkan::DeferredReleaseEntry e;
+ e.type = QRhiVulkan::DeferredReleaseEntry::Pipeline;
+ e.lastActiveFrameSlot = lastActiveFrameSlot;
+
+ e.pipelineState.pipeline = pipeline;
+ e.pipelineState.layout = layout;
+
+ pipeline = VK_NULL_HANDLE;
+ layout = VK_NULL_HANDLE;
+
+ QRHI_RES_RHI(QRhiVulkan);
+ rhiD->releaseQueue.append(e);
+
+ rhiD->unregisterResource(this);
+}
+
+bool QVkGraphicsPipeline::build()
+{
+ if (pipeline)
+ release();
+
+ QRHI_RES_RHI(QRhiVulkan);
+ if (!rhiD->ensurePipelineCache())
+ return false;
+
+ VkPipelineLayoutCreateInfo pipelineLayoutInfo;
+ memset(&pipelineLayoutInfo, 0, sizeof(pipelineLayoutInfo));
+ pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+ pipelineLayoutInfo.setLayoutCount = 1;
+ QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, m_shaderResourceBindings);
+ Q_ASSERT(m_shaderResourceBindings && srbD->layout);
+ pipelineLayoutInfo.pSetLayouts = &srbD->layout;
+ VkResult err = rhiD->df->vkCreatePipelineLayout(rhiD->dev, &pipelineLayoutInfo, nullptr, &layout);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create pipeline layout: %d", err);
+ return false;
+ }
+
+ VkGraphicsPipelineCreateInfo pipelineInfo;
+ memset(&pipelineInfo, 0, sizeof(pipelineInfo));
+ pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
+
+ QVarLengthArray<VkShaderModule, 4> shaders;
+ QVarLengthArray<VkPipelineShaderStageCreateInfo, 4> shaderStageCreateInfos;
+ for (const QRhiShaderStage &shaderStage : m_shaderStages) {
+ const QShader bakedShader = shaderStage.shader();
+ const QShaderCode spirv = bakedShader.shader({ QShader::SpirvShader, 100, shaderStage.shaderVariant() });
+ if (spirv.shader().isEmpty()) {
+ qWarning() << "No SPIR-V 1.0 shader code found in baked shader" << bakedShader;
+ return false;
+ }
+ VkShaderModule shader = rhiD->createShader(spirv.shader());
+ if (shader) {
+ shaders.append(shader);
+ VkPipelineShaderStageCreateInfo shaderInfo;
+ memset(&shaderInfo, 0, sizeof(shaderInfo));
+ shaderInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+ shaderInfo.stage = toVkShaderStage(shaderStage.type());
+ shaderInfo.module = shader;
+ shaderInfo.pName = spirv.entryPoint().constData();
+ shaderStageCreateInfos.append(shaderInfo);
+ }
+ }
+ pipelineInfo.stageCount = shaderStageCreateInfos.count();
+ pipelineInfo.pStages = shaderStageCreateInfos.constData();
+
+ const QVector<QRhiVertexInputBinding> bindings = m_vertexInputLayout.bindings();
+ QVarLengthArray<VkVertexInputBindingDescription, 4> vertexBindings;
+ QVarLengthArray<VkVertexInputBindingDivisorDescriptionEXT> nonOneStepRates;
+ for (int i = 0, ie = bindings.count(); i != ie; ++i) {
+ const QRhiVertexInputBinding &binding(bindings[i]);
+ VkVertexInputBindingDescription bindingInfo = {
+ uint32_t(i),
+ binding.stride(),
+ binding.classification() == QRhiVertexInputBinding::PerVertex
+ ? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE
+ };
+ if (binding.classification() == QRhiVertexInputBinding::PerInstance
+ && binding.instanceStepRate() != 1)
+ {
+ if (rhiD->vertexAttribDivisorAvailable) {
+ nonOneStepRates.append({ uint32_t(i), uint32_t(binding.instanceStepRate()) });
+ } else {
+ qWarning("QRhiVulkan: Instance step rates other than 1 not supported without "
+ "VK_EXT_vertex_attribute_divisor on the device and "
+ "VK_KHR_get_physical_device_properties2 on the instance");
+ }
+ }
+ vertexBindings.append(bindingInfo);
+ }
+ const QVector<QRhiVertexInputAttribute> attributes = m_vertexInputLayout.attributes();
+ QVarLengthArray<VkVertexInputAttributeDescription, 4> vertexAttributes;
+ for (const QRhiVertexInputAttribute &attribute : attributes) {
+ VkVertexInputAttributeDescription attributeInfo = {
+ uint32_t(attribute.location()),
+ uint32_t(attribute.binding()),
+ toVkAttributeFormat(attribute.format()),
+ attribute.offset()
+ };
+ vertexAttributes.append(attributeInfo);
+ }
+ VkPipelineVertexInputStateCreateInfo vertexInputInfo;
+ memset(&vertexInputInfo, 0, sizeof(vertexInputInfo));
+ vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+ vertexInputInfo.vertexBindingDescriptionCount = vertexBindings.count();
+ vertexInputInfo.pVertexBindingDescriptions = vertexBindings.constData();
+ vertexInputInfo.vertexAttributeDescriptionCount = vertexAttributes.count();
+ vertexInputInfo.pVertexAttributeDescriptions = vertexAttributes.constData();
+ VkPipelineVertexInputDivisorStateCreateInfoEXT divisorInfo;
+ if (!nonOneStepRates.isEmpty()) {
+ memset(&divisorInfo, 0, sizeof(divisorInfo));
+ divisorInfo.sType = VkStructureType(1000190001); // VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT
+ divisorInfo.vertexBindingDivisorCount = nonOneStepRates.count();
+ divisorInfo.pVertexBindingDivisors = nonOneStepRates.constData();
+ vertexInputInfo.pNext = &divisorInfo;
+ }
+ pipelineInfo.pVertexInputState = &vertexInputInfo;
+
+ QVarLengthArray<VkDynamicState, 8> dynEnable;
+ dynEnable << VK_DYNAMIC_STATE_VIEWPORT;
+ dynEnable << VK_DYNAMIC_STATE_SCISSOR; // ignore UsesScissor - Vulkan requires a scissor for the viewport always
+ if (m_flags.testFlag(QRhiGraphicsPipeline::UsesBlendConstants))
+ dynEnable << VK_DYNAMIC_STATE_BLEND_CONSTANTS;
+ if (m_flags.testFlag(QRhiGraphicsPipeline::UsesStencilRef))
+ dynEnable << VK_DYNAMIC_STATE_STENCIL_REFERENCE;
+
+ VkPipelineDynamicStateCreateInfo dynamicInfo;
+ memset(&dynamicInfo, 0, sizeof(dynamicInfo));
+ dynamicInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
+ dynamicInfo.dynamicStateCount = dynEnable.count();
+ dynamicInfo.pDynamicStates = dynEnable.constData();
+ pipelineInfo.pDynamicState = &dynamicInfo;
+
+ VkPipelineViewportStateCreateInfo viewportInfo;
+ memset(&viewportInfo, 0, sizeof(viewportInfo));
+ viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
+ viewportInfo.viewportCount = viewportInfo.scissorCount = 1;
+ pipelineInfo.pViewportState = &viewportInfo;
+
+ VkPipelineInputAssemblyStateCreateInfo inputAsmInfo;
+ memset(&inputAsmInfo, 0, sizeof(inputAsmInfo));
+ inputAsmInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
+ inputAsmInfo.topology = toVkTopology(m_topology);
+ inputAsmInfo.primitiveRestartEnable = (m_topology == TriangleStrip || m_topology == LineStrip);
+ pipelineInfo.pInputAssemblyState = &inputAsmInfo;
+
+ VkPipelineRasterizationStateCreateInfo rastInfo;
+ memset(&rastInfo, 0, sizeof(rastInfo));
+ rastInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
+ rastInfo.cullMode = toVkCullMode(m_cullMode);
+ rastInfo.frontFace = toVkFrontFace(m_frontFace);
+ rastInfo.lineWidth = 1.0f;
+ pipelineInfo.pRasterizationState = &rastInfo;
+
+ VkPipelineMultisampleStateCreateInfo msInfo;
+ memset(&msInfo, 0, sizeof(msInfo));
+ msInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
+ msInfo.rasterizationSamples = rhiD->effectiveSampleCount(m_sampleCount);
+ pipelineInfo.pMultisampleState = &msInfo;
+
+ VkPipelineDepthStencilStateCreateInfo dsInfo;
+ memset(&dsInfo, 0, sizeof(dsInfo));
+ dsInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
+ dsInfo.depthTestEnable = m_depthTest;
+ dsInfo.depthWriteEnable = m_depthWrite;
+ dsInfo.depthCompareOp = toVkCompareOp(m_depthOp);
+ dsInfo.stencilTestEnable = m_stencilTest;
+ if (m_stencilTest) {
+ fillVkStencilOpState(&dsInfo.front, m_stencilFront);
+ dsInfo.front.compareMask = m_stencilReadMask;
+ dsInfo.front.writeMask = m_stencilWriteMask;
+ fillVkStencilOpState(&dsInfo.back, m_stencilBack);
+ dsInfo.back.compareMask = m_stencilReadMask;
+ dsInfo.back.writeMask = m_stencilWriteMask;
+ }
+ pipelineInfo.pDepthStencilState = &dsInfo;
+
+ VkPipelineColorBlendStateCreateInfo blendInfo;
+ memset(&blendInfo, 0, sizeof(blendInfo));
+ blendInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
+ QVarLengthArray<VkPipelineColorBlendAttachmentState, 4> vktargetBlends;
+ for (const QRhiGraphicsPipeline::TargetBlend &b : qAsConst(m_targetBlends)) {
+ VkPipelineColorBlendAttachmentState blend;
+ memset(&blend, 0, sizeof(blend));
+ blend.blendEnable = b.enable;
+ blend.srcColorBlendFactor = toVkBlendFactor(b.srcColor);
+ blend.dstColorBlendFactor = toVkBlendFactor(b.dstColor);
+ blend.colorBlendOp = toVkBlendOp(b.opColor);
+ blend.srcAlphaBlendFactor = toVkBlendFactor(b.srcAlpha);
+ blend.dstAlphaBlendFactor = toVkBlendFactor(b.dstAlpha);
+ blend.alphaBlendOp = toVkBlendOp(b.opAlpha);
+ blend.colorWriteMask = toVkColorComponents(b.colorWrite);
+ vktargetBlends.append(blend);
+ }
+ if (vktargetBlends.isEmpty()) {
+ VkPipelineColorBlendAttachmentState blend;
+ memset(&blend, 0, sizeof(blend));
+ blend.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT
+ | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
+ vktargetBlends.append(blend);
+ }
+ blendInfo.attachmentCount = vktargetBlends.count();
+ blendInfo.pAttachments = vktargetBlends.constData();
+ pipelineInfo.pColorBlendState = &blendInfo;
+
+ pipelineInfo.layout = layout;
+
+ Q_ASSERT(m_renderPassDesc && QRHI_RES(const QVkRenderPassDescriptor, m_renderPassDesc)->rp);
+ pipelineInfo.renderPass = QRHI_RES(const QVkRenderPassDescriptor, m_renderPassDesc)->rp;
+
+ err = rhiD->df->vkCreateGraphicsPipelines(rhiD->dev, rhiD->pipelineCache, 1, &pipelineInfo, nullptr, &pipeline);
+
+ for (VkShaderModule shader : shaders)
+ rhiD->df->vkDestroyShaderModule(rhiD->dev, shader, nullptr);
+
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create graphics pipeline: %d", err);
+ return false;
+ }
+
+ lastActiveFrameSlot = -1;
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+QVkComputePipeline::QVkComputePipeline(QRhiImplementation *rhi)
+ : QRhiComputePipeline(rhi)
+{
+}
+
+QVkComputePipeline::~QVkComputePipeline()
+{
+ release();
+}
+
+void QVkComputePipeline::release()
+{
+ if (!pipeline && !layout)
+ return;
+
+ QRhiVulkan::DeferredReleaseEntry e;
+ e.type = QRhiVulkan::DeferredReleaseEntry::Pipeline;
+ e.lastActiveFrameSlot = lastActiveFrameSlot;
+
+ e.pipelineState.pipeline = pipeline;
+ e.pipelineState.layout = layout;
+
+ pipeline = VK_NULL_HANDLE;
+ layout = VK_NULL_HANDLE;
+
+ QRHI_RES_RHI(QRhiVulkan);
+ rhiD->releaseQueue.append(e);
+
+ rhiD->unregisterResource(this);
+}
+
+bool QVkComputePipeline::build()
+{
+ if (pipeline)
+ release();
+
+ QRHI_RES_RHI(QRhiVulkan);
+ if (!rhiD->ensurePipelineCache())
+ return false;
+
+ VkPipelineLayoutCreateInfo pipelineLayoutInfo;
+ memset(&pipelineLayoutInfo, 0, sizeof(pipelineLayoutInfo));
+ pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+ pipelineLayoutInfo.setLayoutCount = 1;
+ QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, m_shaderResourceBindings);
+ Q_ASSERT(m_shaderResourceBindings && srbD->layout);
+ pipelineLayoutInfo.pSetLayouts = &srbD->layout;
+ VkResult err = rhiD->df->vkCreatePipelineLayout(rhiD->dev, &pipelineLayoutInfo, nullptr, &layout);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create pipeline layout: %d", err);
+ return false;
+ }
+
+ VkComputePipelineCreateInfo pipelineInfo;
+ memset(&pipelineInfo, 0, sizeof(pipelineInfo));
+ pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
+ pipelineInfo.layout = layout;
+
+ if (m_shaderStage.type() != QRhiShaderStage::Compute) {
+ qWarning("Compute pipeline requires a compute shader stage");
+ return false;
+ }
+ const QShader bakedShader = m_shaderStage.shader();
+ const QShaderCode spirv = bakedShader.shader({ QShader::SpirvShader, 100, m_shaderStage.shaderVariant() });
+ if (spirv.shader().isEmpty()) {
+ qWarning() << "No SPIR-V 1.0 shader code found in baked shader" << bakedShader;
+ return false;
+ }
+ if (bakedShader.stage() != QShader::ComputeStage) {
+ qWarning() << bakedShader << "is not a compute shader";
+ return false;
+ }
+ VkShaderModule shader = rhiD->createShader(spirv.shader());
+ VkPipelineShaderStageCreateInfo shaderInfo;
+ memset(&shaderInfo, 0, sizeof(shaderInfo));
+ shaderInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+ shaderInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT;
+ shaderInfo.module = shader;
+ shaderInfo.pName = spirv.entryPoint().constData();
+ pipelineInfo.stage = shaderInfo;
+
+ err = rhiD->df->vkCreateComputePipelines(rhiD->dev, rhiD->pipelineCache, 1, &pipelineInfo, nullptr, &pipeline);
+ rhiD->df->vkDestroyShaderModule(rhiD->dev, shader, nullptr);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create graphics pipeline: %d", err);
+ return false;
+ }
+
+ lastActiveFrameSlot = -1;
+ generation += 1;
+ rhiD->registerResource(this);
+ return true;
+}
+
+QVkCommandBuffer::QVkCommandBuffer(QRhiImplementation *rhi)
+ : QRhiCommandBuffer(rhi)
+{
+ resetState();
+}
+
+QVkCommandBuffer::~QVkCommandBuffer()
+{
+ release();
+}
+
+void QVkCommandBuffer::release()
+{
+ // nothing to do here, cb is not owned by us
+}
+
+QVkSwapChain::QVkSwapChain(QRhiImplementation *rhi)
+ : QRhiSwapChain(rhi),
+ rtWrapper(rhi),
+ cbWrapper(rhi)
+{
+}
+
+QVkSwapChain::~QVkSwapChain()
+{
+ release();
+}
+
+void QVkSwapChain::release()
+{
+ if (sc == VK_NULL_HANDLE)
+ return;
+
+ QRHI_RES_RHI(QRhiVulkan);
+ rhiD->swapchains.remove(this);
+ rhiD->releaseSwapChainResources(this);
+ surface = lastConnectedSurface = VK_NULL_HANDLE;
+
+ QRHI_PROF;
+ QRHI_PROF_F(releaseSwapChain(this));
+
+ rhiD->unregisterResource(this);
+}
+
+QRhiCommandBuffer *QVkSwapChain::currentFrameCommandBuffer()
+{
+ return &cbWrapper;
+}
+
+QRhiRenderTarget *QVkSwapChain::currentFrameRenderTarget()
+{
+ return &rtWrapper;
+}
+
+QSize QVkSwapChain::surfacePixelSize()
+{
+ if (!ensureSurface())
+ return QSize();
+
+ // The size from the QWindow may not exactly match the surface... so if a
+ // size is reported from the surface, use that.
+ VkSurfaceCapabilitiesKHR surfaceCaps;
+ memset(&surfaceCaps, 0, sizeof(surfaceCaps));
+ QRHI_RES_RHI(QRhiVulkan);
+ rhiD->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(rhiD->physDev, surface, &surfaceCaps);
+ VkExtent2D bufferSize = surfaceCaps.currentExtent;
+ if (bufferSize.width == quint32(-1)) {
+ Q_ASSERT(bufferSize.height == quint32(-1));
+ return m_window->size() * m_window->devicePixelRatio();
+ }
+ return QSize(bufferSize.width, bufferSize.height);
+}
+
+QRhiRenderPassDescriptor *QVkSwapChain::newCompatibleRenderPassDescriptor()
+{
+ // not yet built so cannot rely on data computed in buildOrResize()
+
+ if (!ensureSurface()) // make sure sampleCount and colorFormat reflect what was requested
+ return nullptr;
+
+ QRHI_RES_RHI(QRhiVulkan);
+ QVkRenderPassDescriptor *rp = new QVkRenderPassDescriptor(m_rhi);
+ if (!rhiD->createDefaultRenderPass(&rp->rp,
+ m_depthStencil != nullptr,
+ samples,
+ colorFormat))
+ {
+ delete rp;
+ return nullptr;
+ }
+
+ rp->ownsRp = true;
+ rhiD->registerResource(rp);
+ return rp;
+}
+
+static inline bool isSrgbFormat(VkFormat format)
+{
+ switch (format) {
+ case VK_FORMAT_R8_SRGB:
+ Q_FALLTHROUGH();
+ case VK_FORMAT_R8G8_SRGB:
+ Q_FALLTHROUGH();
+ case VK_FORMAT_R8G8B8_SRGB:
+ Q_FALLTHROUGH();
+ case VK_FORMAT_B8G8R8_SRGB:
+ Q_FALLTHROUGH();
+ case VK_FORMAT_R8G8B8A8_SRGB:
+ Q_FALLTHROUGH();
+ case VK_FORMAT_B8G8R8A8_SRGB:
+ Q_FALLTHROUGH();
+ case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool QVkSwapChain::ensureSurface()
+{
+ // Do nothing when already done, however window may change so check the
+ // surface is still the same. Some of the queries below are very expensive
+ // with some implementations so it is important to do the rest only once
+ // per surface.
+
+ Q_ASSERT(m_window);
+ VkSurfaceKHR surf = QVulkanInstance::surfaceForWindow(m_window);
+ if (!surf) {
+ qWarning("Failed to get surface for window");
+ return false;
+ }
+ if (surface == surf)
+ return true;
+
+ surface = surf;
+
+ QRHI_RES_RHI(QRhiVulkan);
+ if (rhiD->gfxQueueFamilyIdx != -1) {
+ if (!rhiD->inst->supportsPresent(rhiD->physDev, rhiD->gfxQueueFamilyIdx, m_window)) {
+ qWarning("Presenting not supported on this window");
+ return false;
+ }
+ }
+
+ if (!rhiD->vkGetPhysicalDeviceSurfaceCapabilitiesKHR) {
+ rhiD->vkGetPhysicalDeviceSurfaceCapabilitiesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR>(
+ rhiD->inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceCapabilitiesKHR"));
+ rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR>(
+ rhiD->inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfaceFormatsKHR"));
+ rhiD->vkGetPhysicalDeviceSurfacePresentModesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR>(
+ rhiD->inst->getInstanceProcAddr("vkGetPhysicalDeviceSurfacePresentModesKHR"));
+ if (!rhiD->vkGetPhysicalDeviceSurfaceCapabilitiesKHR
+ || !rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR
+ || !rhiD->vkGetPhysicalDeviceSurfacePresentModesKHR)
+ {
+ qWarning("Physical device surface queries not available");
+ return false;
+ }
+ }
+
+ quint32 formatCount = 0;
+ rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR(rhiD->physDev, surface, &formatCount, nullptr);
+ QVector<VkSurfaceFormatKHR> formats(formatCount);
+ if (formatCount)
+ rhiD->vkGetPhysicalDeviceSurfaceFormatsKHR(rhiD->physDev, surface, &formatCount, formats.data());
+
+ const bool srgbRequested = m_flags.testFlag(sRGB);
+ for (quint32 i = 0; i < formatCount; ++i) {
+ if (formats[i].format != VK_FORMAT_UNDEFINED && srgbRequested == isSrgbFormat(formats[i].format)) {
+ colorFormat = formats[i].format;
+ colorSpace = formats[i].colorSpace;
+ break;
+ }
+ }
+
+ samples = rhiD->effectiveSampleCount(m_sampleCount);
+
+ quint32 presModeCount = 0;
+ rhiD->vkGetPhysicalDeviceSurfacePresentModesKHR(rhiD->physDev, surface, &presModeCount, nullptr);
+ QVector<VkPresentModeKHR> presModes(presModeCount);
+ rhiD->vkGetPhysicalDeviceSurfacePresentModesKHR(rhiD->physDev, surface, &presModeCount, presModes.data());
+ supportedPresentationModes = presModes;
+
+ return true;
+}
+
+bool QVkSwapChain::buildOrResize()
+{
+ QRHI_RES_RHI(QRhiVulkan);
+ const bool needsRegistration = !window || window != m_window;
+
+ // Can be called multiple times due to window resizes - that is not the
+ // same as a simple release+build (as with other resources). Thus no
+ // release() here. See recreateSwapChain().
+
+ // except if the window actually changes
+ if (window && window != m_window)
+ release();
+
+ window = m_window;
+ m_currentPixelSize = surfacePixelSize();
+ pixelSize = m_currentPixelSize;
+
+ if (!rhiD->recreateSwapChain(this)) {
+ qWarning("Failed to create new swapchain");
+ return false;
+ }
+
+ if (needsRegistration)
+ rhiD->swapchains.insert(this);
+
+ if (m_depthStencil && m_depthStencil->sampleCount() != m_sampleCount) {
+ qWarning("Depth-stencil buffer's sampleCount (%d) does not match color buffers' sample count (%d). Expect problems.",
+ m_depthStencil->sampleCount(), m_sampleCount);
+ }
+ if (m_depthStencil && m_depthStencil->pixelSize() != pixelSize) {
+ qWarning("Depth-stencil buffer's size (%dx%d) does not match the surface size (%dx%d). Expect problems.",
+ m_depthStencil->pixelSize().width(), m_depthStencil->pixelSize().height(),
+ pixelSize.width(), pixelSize.height());
+ }
+
+ if (!m_renderPassDesc)
+ qWarning("QVkSwapChain: No renderpass descriptor set. See newCompatibleRenderPassDescriptor() and setRenderPassDescriptor().");
+
+ rtWrapper.d.rp = QRHI_RES(QVkRenderPassDescriptor, m_renderPassDesc);
+ Q_ASSERT(rtWrapper.d.rp && rtWrapper.d.rp->rp);
+
+ rtWrapper.d.pixelSize = pixelSize;
+ rtWrapper.d.dpr = window->devicePixelRatio();
+ rtWrapper.d.sampleCount = samples;
+ rtWrapper.d.colorAttCount = 1;
+ if (m_depthStencil) {
+ rtWrapper.d.dsAttCount = 1;
+ ds = QRHI_RES(QVkRenderBuffer, m_depthStencil);
+ } else {
+ rtWrapper.d.dsAttCount = 0;
+ ds = nullptr;
+ }
+ if (samples > VK_SAMPLE_COUNT_1_BIT)
+ rtWrapper.d.resolveAttCount = 1;
+ else
+ rtWrapper.d.resolveAttCount = 0;
+
+ for (int i = 0; i < bufferCount; ++i) {
+ QVkSwapChain::ImageResources &image(imageRes[i]);
+ VkImageView views[3] = { // color, ds, resolve
+ samples > VK_SAMPLE_COUNT_1_BIT ? image.msaaImageView : image.imageView,
+ ds ? ds->imageView : VK_NULL_HANDLE,
+ samples > VK_SAMPLE_COUNT_1_BIT ? image.imageView : VK_NULL_HANDLE
+ };
+
+ VkFramebufferCreateInfo fbInfo;
+ memset(&fbInfo, 0, sizeof(fbInfo));
+ fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+ fbInfo.renderPass = rtWrapper.d.rp->rp;
+ fbInfo.attachmentCount = rtWrapper.d.colorAttCount + rtWrapper.d.dsAttCount + rtWrapper.d.resolveAttCount;
+ fbInfo.pAttachments = views;
+ fbInfo.width = pixelSize.width();
+ fbInfo.height = pixelSize.height();
+ fbInfo.layers = 1;
+
+ VkResult err = rhiD->df->vkCreateFramebuffer(rhiD->dev, &fbInfo, nullptr, &image.fb);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create framebuffer: %d", err);
+ return false;
+ }
+ }
+
+ frameCount = 0;
+
+ QRHI_PROF;
+ QRHI_PROF_F(resizeSwapChain(this, QVK_FRAMES_IN_FLIGHT, samples > VK_SAMPLE_COUNT_1_BIT ? QVK_FRAMES_IN_FLIGHT : 0, samples));
+
+ if (needsRegistration)
+ rhiD->registerResource(this);
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/rhi/qrhivulkan_p.h b/src/gui/rhi/qrhivulkan_p.h
new file mode 100644
index 0000000000..545ef5ad72
--- /dev/null
+++ b/src/gui/rhi/qrhivulkan_p.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHIVULKAN_H
+#define QRHIVULKAN_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qrhi_p.h>
+#include <QtGui/qvulkaninstance.h> // this is where vulkan.h gets pulled in
+
+QT_BEGIN_NAMESPACE
+
+struct Q_GUI_EXPORT QRhiVulkanInitParams : public QRhiInitParams
+{
+ QVulkanInstance *inst = nullptr;
+ QWindow *window = nullptr;
+};
+
+struct Q_GUI_EXPORT QRhiVulkanNativeHandles : public QRhiNativeHandles
+{
+ VkPhysicalDevice physDev = VK_NULL_HANDLE;
+ VkDevice dev = VK_NULL_HANDLE;
+ int gfxQueueFamilyIdx = -1;
+ VkQueue gfxQueue = VK_NULL_HANDLE;
+ VkCommandPool cmdPool = VK_NULL_HANDLE;
+ void *vmemAllocator = nullptr;
+};
+
+struct Q_GUI_EXPORT QRhiVulkanTextureNativeHandles : public QRhiNativeHandles
+{
+ VkImage image = VK_NULL_HANDLE;
+ VkImageLayout layout = VK_IMAGE_LAYOUT_GENERAL;
+};
+
+struct Q_GUI_EXPORT QRhiVulkanCommandBufferNativeHandles : public QRhiNativeHandles
+{
+ VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhivulkan_p_p.h b/src/gui/rhi/qrhivulkan_p_p.h
new file mode 100644
index 0000000000..cec9016603
--- /dev/null
+++ b/src/gui/rhi/qrhivulkan_p_p.h
@@ -0,0 +1,915 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHIVULKAN_P_H
+#define QRHIVULKAN_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qrhivulkan_p.h"
+#include "qrhi_p_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QVulkanFunctions;
+class QVulkanDeviceFunctions;
+
+static const int QVK_FRAMES_IN_FLIGHT = 2;
+
+static const int QVK_DESC_SETS_PER_POOL = 128;
+static const int QVK_UNIFORM_BUFFERS_PER_POOL = 256;
+static const int QVK_COMBINED_IMAGE_SAMPLERS_PER_POOL = 256;
+static const int QVK_STORAGE_BUFFERS_PER_POOL = 128;
+static const int QVK_STORAGE_IMAGES_PER_POOL = 128;
+
+static const int QVK_MAX_ACTIVE_TIMESTAMP_PAIRS = 16;
+
+// no vk_mem_alloc.h available here, void* is good enough
+typedef void * QVkAlloc;
+typedef void * QVkAllocator;
+
+struct QVkBuffer : public QRhiBuffer
+{
+ QVkBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size);
+ ~QVkBuffer();
+ void release() override;
+ bool build() override;
+
+ VkBuffer buffers[QVK_FRAMES_IN_FLIGHT];
+ QVkAlloc allocations[QVK_FRAMES_IN_FLIGHT];
+ QVector<QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate> pendingDynamicUpdates[QVK_FRAMES_IN_FLIGHT];
+ VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT];
+ QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT];
+ struct UsageState {
+ VkAccessFlags access = 0;
+ VkPipelineStageFlags stage = 0;
+ };
+ UsageState usageState[QVK_FRAMES_IN_FLIGHT];
+ int lastActiveFrameSlot = -1;
+ uint generation = 0;
+ friend class QRhiVulkan;
+};
+
+struct QVkTexture;
+
+struct QVkRenderBuffer : public QRhiRenderBuffer
+{
+ QVkRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
+ int sampleCount, Flags flags);
+ ~QVkRenderBuffer();
+ void release() override;
+ bool build() override;
+ QRhiTexture::Format backingFormat() const override;
+
+ VkDeviceMemory memory = VK_NULL_HANDLE;
+ VkImage image = VK_NULL_HANDLE;
+ VkImageView imageView = VK_NULL_HANDLE;
+ VkSampleCountFlagBits samples;
+ QVkTexture *backingTexture = nullptr;
+ VkFormat vkformat;
+ int lastActiveFrameSlot = -1;
+ friend class QRhiVulkan;
+};
+
+struct QVkTexture : public QRhiTexture
+{
+ QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
+ int sampleCount, Flags flags);
+ ~QVkTexture();
+ void release() override;
+ bool build() override;
+ bool buildFrom(const QRhiNativeHandles *src) override;
+ const QRhiNativeHandles *nativeHandles() override;
+
+ bool prepareBuild(QSize *adjustedSize = nullptr);
+ bool finishBuild();
+ VkImageView imageViewForLevel(int level);
+
+ VkImage image = VK_NULL_HANDLE;
+ VkImageView imageView = VK_NULL_HANDLE;
+ QVkAlloc imageAlloc = nullptr;
+ VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT];
+ QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT];
+ VkImageView perLevelImageViews[QRhi::MAX_LEVELS];
+ bool owns = true;
+ QRhiVulkanTextureNativeHandles nativeHandlesStruct;
+ struct UsageState {
+ // no tracking of subresource layouts (some operations can keep
+ // subresources in different layouts for some time, but that does not
+ // need to be kept track of)
+ VkImageLayout layout;
+ VkAccessFlags access;
+ VkPipelineStageFlags stage;
+ };
+ UsageState usageState;
+ VkFormat vkformat;
+ uint mipLevelCount = 0;
+ VkSampleCountFlagBits samples;
+ int lastActiveFrameSlot = -1;
+ uint generation = 0;
+ friend class QRhiVulkan;
+};
+
+struct QVkSampler : public QRhiSampler
+{
+ QVkSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
+ AddressMode u, AddressMode v);
+ ~QVkSampler();
+ void release() override;
+ bool build() override;
+
+ VkSampler sampler = VK_NULL_HANDLE;
+ int lastActiveFrameSlot = -1;
+ uint generation = 0;
+ friend class QRhiVulkan;
+};
+
+struct QVkRenderPassDescriptor : public QRhiRenderPassDescriptor
+{
+ QVkRenderPassDescriptor(QRhiImplementation *rhi);
+ ~QVkRenderPassDescriptor();
+ void release() override;
+
+ VkRenderPass rp = VK_NULL_HANDLE;
+ bool ownsRp = false;
+ int lastActiveFrameSlot = -1;
+};
+
+struct QVkRenderTargetData
+{
+ VkFramebuffer fb = VK_NULL_HANDLE;
+ QVkRenderPassDescriptor *rp = nullptr;
+ QSize pixelSize;
+ float dpr = 1;
+ int sampleCount = 1;
+ int colorAttCount = 0;
+ int dsAttCount = 0;
+ int resolveAttCount = 0;
+ static const int MAX_COLOR_ATTACHMENTS = 8;
+};
+
+struct QVkReferenceRenderTarget : public QRhiRenderTarget
+{
+ QVkReferenceRenderTarget(QRhiImplementation *rhi);
+ ~QVkReferenceRenderTarget();
+ void release() override;
+
+ QSize pixelSize() const override;
+ float devicePixelRatio() const override;
+ int sampleCount() const override;
+
+ QVkRenderTargetData d;
+};
+
+struct QVkTextureRenderTarget : public QRhiTextureRenderTarget
+{
+ QVkTextureRenderTarget(QRhiImplementation *rhi, const QRhiTextureRenderTargetDescription &desc, Flags flags);
+ ~QVkTextureRenderTarget();
+ void release() override;
+
+ QSize pixelSize() const override;
+ float devicePixelRatio() const override;
+ int sampleCount() const override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+ bool build() override;
+
+ QVkRenderTargetData d;
+ VkImageView rtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
+ VkImageView resrtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
+ int lastActiveFrameSlot = -1;
+ friend class QRhiVulkan;
+};
+
+struct QVkShaderResourceBindings : public QRhiShaderResourceBindings
+{
+ QVkShaderResourceBindings(QRhiImplementation *rhi);
+ ~QVkShaderResourceBindings();
+ void release() override;
+ bool build() override;
+
+ QVector<QRhiShaderResourceBinding> sortedBindings;
+ int poolIndex = -1;
+ VkDescriptorSetLayout layout = VK_NULL_HANDLE;
+ VkDescriptorSet descSets[QVK_FRAMES_IN_FLIGHT]; // multiple sets to support dynamic buffers
+ int lastActiveFrameSlot = -1;
+ uint generation = 0;
+
+ // Keep track of the generation number of each referenced QRhi* to be able
+ // to detect that the underlying descriptor set became out of date and they
+ // need to be written again with the up-to-date VkBuffer etc. objects.
+ struct BoundUniformBufferData {
+ quint64 id;
+ uint generation;
+ };
+ struct BoundSampledTextureData {
+ quint64 texId;
+ uint texGeneration;
+ quint64 samplerId;
+ uint samplerGeneration;
+ };
+ struct BoundStorageImageData {
+ quint64 id;
+ uint generation;
+ };
+ struct BoundStorageBufferData {
+ quint64 id;
+ uint generation;
+ };
+ struct BoundResourceData {
+ union {
+ BoundUniformBufferData ubuf;
+ BoundSampledTextureData stex;
+ BoundStorageImageData simage;
+ BoundStorageBufferData sbuf;
+ };
+ };
+ QVector<BoundResourceData> boundResourceData[QVK_FRAMES_IN_FLIGHT];
+
+ friend class QRhiVulkan;
+};
+
+Q_DECLARE_TYPEINFO(QVkShaderResourceBindings::BoundResourceData, Q_MOVABLE_TYPE);
+
+struct QVkGraphicsPipeline : public QRhiGraphicsPipeline
+{
+ QVkGraphicsPipeline(QRhiImplementation *rhi);
+ ~QVkGraphicsPipeline();
+ void release() override;
+ bool build() override;
+
+ VkPipelineLayout layout = VK_NULL_HANDLE;
+ VkPipeline pipeline = VK_NULL_HANDLE;
+ int lastActiveFrameSlot = -1;
+ uint generation = 0;
+ friend class QRhiVulkan;
+};
+
+struct QVkComputePipeline : public QRhiComputePipeline
+{
+ QVkComputePipeline(QRhiImplementation *rhi);
+ ~QVkComputePipeline();
+ void release() override;
+ bool build() override;
+
+ VkPipelineLayout layout = VK_NULL_HANDLE;
+ VkPipeline pipeline = VK_NULL_HANDLE;
+ int lastActiveFrameSlot = -1;
+ uint generation = 0;
+ friend class QRhiVulkan;
+};
+
+struct QVkCommandBuffer : public QRhiCommandBuffer
+{
+ QVkCommandBuffer(QRhiImplementation *rhi);
+ ~QVkCommandBuffer();
+ void release() override;
+
+ VkCommandBuffer cb = VK_NULL_HANDLE;
+ QRhiVulkanCommandBufferNativeHandles nativeHandlesStruct;
+
+ const QRhiNativeHandles *nativeHandles() {
+ nativeHandlesStruct.commandBuffer = cb;
+ return &nativeHandlesStruct;
+ }
+
+ enum PassType {
+ NoPass,
+ RenderPass,
+ ComputePass
+ };
+
+ void resetState() {
+ resetCommands();
+ recordingPass = NoPass;
+ currentTarget = nullptr;
+ resetCachedState();
+ }
+
+ void resetCachedState() {
+ currentGraphicsPipeline = nullptr;
+ currentComputePipeline = nullptr;
+ currentPipelineGeneration = 0;
+ currentGraphicsSrb = nullptr;
+ currentComputeSrb = nullptr;
+ currentSrbGeneration = 0;
+ currentDescSetSlot = -1;
+ currentIndexBuffer = VK_NULL_HANDLE;
+ currentIndexOffset = 0;
+ currentIndexFormat = VK_INDEX_TYPE_UINT16;
+ memset(currentVertexBuffers, 0, sizeof(currentVertexBuffers));
+ memset(currentVertexOffsets, 0, sizeof(currentVertexOffsets));
+ }
+
+ PassType recordingPass;
+ QRhiRenderTarget *currentTarget;
+ QRhiGraphicsPipeline *currentGraphicsPipeline;
+ QRhiComputePipeline *currentComputePipeline;
+ uint currentPipelineGeneration;
+ QRhiShaderResourceBindings *currentGraphicsSrb;
+ QRhiShaderResourceBindings *currentComputeSrb;
+ uint currentSrbGeneration;
+ int currentDescSetSlot;
+ VkBuffer currentIndexBuffer;
+ quint32 currentIndexOffset;
+ VkIndexType currentIndexFormat;
+ static const int VERTEX_INPUT_RESOURCE_SLOT_COUNT = 32;
+ VkBuffer currentVertexBuffers[VERTEX_INPUT_RESOURCE_SLOT_COUNT];
+ quint32 currentVertexOffsets[VERTEX_INPUT_RESOURCE_SLOT_COUNT];
+
+ struct Command {
+ enum Cmd {
+ CopyBuffer,
+ CopyBufferToImage,
+ CopyImage,
+ CopyImageToBuffer,
+ ImageBarrier,
+ BufferBarrier,
+ BlitImage,
+ BeginRenderPass,
+ EndRenderPass,
+ BindPipeline,
+ BindDescriptorSet,
+ BindVertexBuffer,
+ BindIndexBuffer,
+ SetViewport,
+ SetScissor,
+ SetBlendConstants,
+ SetStencilRef,
+ Draw,
+ DrawIndexed,
+ DebugMarkerBegin,
+ DebugMarkerEnd,
+ DebugMarkerInsert,
+ TransitionPassResources,
+ Dispatch
+ };
+ Cmd cmd;
+
+ union Args {
+ struct {
+ VkBuffer src;
+ VkBuffer dst;
+ VkBufferCopy desc;
+ } copyBuffer;
+ struct {
+ VkBuffer src;
+ VkImage dst;
+ VkImageLayout dstLayout;
+ int count;
+ int bufferImageCopyIndex;
+ } copyBufferToImage;
+ struct {
+ VkImage src;
+ VkImageLayout srcLayout;
+ VkImage dst;
+ VkImageLayout dstLayout;
+ VkImageCopy desc;
+ } copyImage;
+ struct {
+ VkImage src;
+ VkImageLayout srcLayout;
+ VkBuffer dst;
+ VkBufferImageCopy desc;
+ } copyImageToBuffer;
+ struct {
+ VkPipelineStageFlags srcStageMask;
+ VkPipelineStageFlags dstStageMask;
+ VkImageMemoryBarrier desc;
+ } imageBarrier;
+ struct {
+ VkPipelineStageFlags srcStageMask;
+ VkPipelineStageFlags dstStageMask;
+ VkBufferMemoryBarrier desc;
+ } bufferBarrier;
+ struct {
+ VkImage src;
+ VkImageLayout srcLayout;
+ VkImage dst;
+ VkImageLayout dstLayout;
+ VkFilter filter;
+ VkImageBlit desc;
+ } blitImage;
+ struct {
+ VkRenderPassBeginInfo desc;
+ int clearValueIndex;
+ } beginRenderPass;
+ struct {
+ } endRenderPass;
+ struct {
+ VkPipelineBindPoint bindPoint;
+ VkPipeline pipeline;
+ } bindPipeline;
+ struct {
+ VkPipelineBindPoint bindPoint;
+ VkPipelineLayout pipelineLayout;
+ VkDescriptorSet descSet;
+ int dynamicOffsetCount;
+ int dynamicOffsetIndex;
+ } bindDescriptorSet;
+ struct {
+ int startBinding;
+ int count;
+ int vertexBufferIndex;
+ int vertexBufferOffsetIndex;
+ } bindVertexBuffer;
+ struct {
+ VkBuffer buf;
+ VkDeviceSize ofs;
+ VkIndexType type;
+ } bindIndexBuffer;
+ struct {
+ VkViewport viewport;
+ } setViewport;
+ struct {
+ VkRect2D scissor;
+ } setScissor;
+ struct {
+ float c[4];
+ } setBlendConstants;
+ struct {
+ uint32_t ref;
+ } setStencilRef;
+ struct {
+ uint32_t vertexCount;
+ uint32_t instanceCount;
+ uint32_t firstVertex;
+ uint32_t firstInstance;
+ } draw;
+ struct {
+ uint32_t indexCount;
+ uint32_t instanceCount;
+ uint32_t firstIndex;
+ int32_t vertexOffset;
+ uint32_t firstInstance;
+ } drawIndexed;
+ struct {
+ VkDebugMarkerMarkerInfoEXT marker;
+ int markerNameIndex;
+ } debugMarkerBegin;
+ struct {
+ } debugMarkerEnd;
+ struct {
+ VkDebugMarkerMarkerInfoEXT marker;
+ } debugMarkerInsert;
+ struct {
+ int trackerIndex;
+ } transitionResources;
+ struct {
+ int x, y, z;
+ } dispatch;
+ } args;
+ };
+ QVector<Command> commands;
+ QVarLengthArray<QRhiPassResourceTracker, 8> passResTrackers;
+ int currentPassResTrackerIndex;
+
+ void resetCommands() {
+ commands.clear();
+ passResTrackers.clear();
+ currentPassResTrackerIndex = -1;
+ resetPools();
+ }
+
+ void resetPools() {
+ pools.clearValue.clear();
+ pools.bufferImageCopy.clear();
+ pools.dynamicOffset.clear();
+ pools.vertexBuffer.clear();
+ pools.vertexBufferOffset.clear();
+ pools.debugMarkerName.clear();
+ }
+
+ struct {
+ QVarLengthArray<VkClearValue, 4> clearValue;
+ QVarLengthArray<VkBufferImageCopy, 16> bufferImageCopy;
+ QVarLengthArray<uint32_t, 4> dynamicOffset;
+ QVarLengthArray<VkBuffer, 4> vertexBuffer;
+ QVarLengthArray<VkDeviceSize, 4> vertexBufferOffset;
+ QVarLengthArray<QByteArray, 4> debugMarkerName;
+ } pools;
+
+ friend class QRhiVulkan;
+};
+
+Q_DECLARE_TYPEINFO(QVkCommandBuffer::Command, Q_MOVABLE_TYPE);
+
+struct QVkSwapChain : public QRhiSwapChain
+{
+ QVkSwapChain(QRhiImplementation *rhi);
+ ~QVkSwapChain();
+ void release() override;
+
+ QRhiCommandBuffer *currentFrameCommandBuffer() override;
+ QRhiRenderTarget *currentFrameRenderTarget() override;
+
+ QSize surfacePixelSize() override;
+
+ QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override;
+ bool buildOrResize() override;
+
+ bool ensureSurface();
+
+ static const quint32 MAX_BUFFER_COUNT = 3;
+
+ QWindow *window = nullptr;
+ QSize pixelSize;
+ bool supportsReadback = false;
+ VkSwapchainKHR sc = VK_NULL_HANDLE;
+ int bufferCount = 0;
+ VkSurfaceKHR surface = VK_NULL_HANDLE;
+ VkSurfaceKHR lastConnectedSurface = VK_NULL_HANDLE;
+ VkFormat colorFormat = VK_FORMAT_B8G8R8A8_UNORM;
+ VkColorSpaceKHR colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
+ QVkRenderBuffer *ds = nullptr;
+ VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT;
+ QVector<VkPresentModeKHR> supportedPresentationModes;
+ VkDeviceMemory msaaImageMem = VK_NULL_HANDLE;
+ QVkReferenceRenderTarget rtWrapper;
+ QVkCommandBuffer cbWrapper;
+
+ struct ImageResources {
+ VkImage image = VK_NULL_HANDLE;
+ VkImageView imageView = VK_NULL_HANDLE;
+ VkFramebuffer fb = VK_NULL_HANDLE;
+ VkImage msaaImage = VK_NULL_HANDLE;
+ VkImageView msaaImageView = VK_NULL_HANDLE;
+ enum LastUse {
+ ScImageUseNone,
+ ScImageUseRender,
+ ScImageUseTransferSource
+ };
+ LastUse lastUse = ScImageUseNone;
+ } imageRes[MAX_BUFFER_COUNT];
+
+ struct FrameResources {
+ VkFence imageFence = VK_NULL_HANDLE;
+ bool imageFenceWaitable = false;
+ VkSemaphore imageSem = VK_NULL_HANDLE;
+ VkSemaphore drawSem = VK_NULL_HANDLE;
+ bool imageAcquired = false;
+ bool imageSemWaitable = false;
+ quint32 imageIndex = 0;
+ VkCommandBuffer cmdBuf = VK_NULL_HANDLE;
+ VkFence cmdFence = VK_NULL_HANDLE;
+ bool cmdFenceWaitable = false;
+ int timestampQueryIndex = -1;
+ } frameRes[QVK_FRAMES_IN_FLIGHT];
+
+ quint32 currentImageIndex = 0; // index in imageRes
+ quint32 currentFrameSlot = 0; // index in frameRes
+ int frameCount = 0;
+
+ friend class QRhiVulkan;
+};
+
+class QRhiVulkan : public QRhiImplementation
+{
+public:
+ QRhiVulkan(QRhiVulkanInitParams *params, QRhiVulkanNativeHandles *importDevice = nullptr);
+
+ bool create(QRhi::Flags flags) override;
+ void destroy() override;
+
+ QRhiGraphicsPipeline *createGraphicsPipeline() override;
+ QRhiComputePipeline *createComputePipeline() override;
+ QRhiShaderResourceBindings *createShaderResourceBindings() override;
+ QRhiBuffer *createBuffer(QRhiBuffer::Type type,
+ QRhiBuffer::UsageFlags usage,
+ int size) override;
+ QRhiRenderBuffer *createRenderBuffer(QRhiRenderBuffer::Type type,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiRenderBuffer::Flags flags) override;
+ QRhiTexture *createTexture(QRhiTexture::Format format,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhiTexture::Flags flags) override;
+ QRhiSampler *createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
+ QRhiSampler::Filter mipmapMode,
+ QRhiSampler:: AddressMode u, QRhiSampler::AddressMode v) override;
+
+ QRhiTextureRenderTarget *createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
+ QRhiTextureRenderTarget::Flags flags) override;
+
+ QRhiSwapChain *createSwapChain() override;
+ QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
+ QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
+ QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override;
+ QRhi::FrameOpResult endOffscreenFrame() override;
+ QRhi::FrameOpResult finish() override;
+
+ void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+
+ void beginPass(QRhiCommandBuffer *cb,
+ QRhiRenderTarget *rt,
+ const QColor &colorClearValue,
+ const QRhiDepthStencilClearValue &depthStencilClearValue,
+ QRhiResourceUpdateBatch *resourceUpdates) override;
+ void endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+
+ void setGraphicsPipeline(QRhiCommandBuffer *cb,
+ QRhiGraphicsPipeline *ps) override;
+
+ void setShaderResources(QRhiCommandBuffer *cb,
+ QRhiShaderResourceBindings *srb,
+ int dynamicOffsetCount,
+ const QRhiCommandBuffer::DynamicOffset *dynamicOffsets) override;
+
+ void setVertexInput(QRhiCommandBuffer *cb,
+ int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
+ QRhiBuffer *indexBuf, quint32 indexOffset,
+ QRhiCommandBuffer::IndexFormat indexFormat) override;
+
+ void setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport) override;
+ void setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor) override;
+ void setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) override;
+ void setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) override;
+
+ void draw(QRhiCommandBuffer *cb, quint32 vertexCount,
+ quint32 instanceCount, quint32 firstVertex, quint32 firstInstance) override;
+
+ void drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
+ quint32 instanceCount, quint32 firstIndex,
+ qint32 vertexOffset, quint32 firstInstance) override;
+
+ void debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) override;
+ void debugMarkEnd(QRhiCommandBuffer *cb) override;
+ void debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) override;
+
+ void beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+ void endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
+ void setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps) override;
+ void dispatch(QRhiCommandBuffer *cb, int x, int y, int z) override;
+
+ const QRhiNativeHandles *nativeHandles(QRhiCommandBuffer *cb) override;
+ void beginExternal(QRhiCommandBuffer *cb) override;
+ void endExternal(QRhiCommandBuffer *cb) override;
+
+ QVector<int> supportedSampleCounts() const override;
+ int ubufAlignment() const override;
+ bool isYUpInFramebuffer() const override;
+ bool isYUpInNDC() const override;
+ bool isClipDepthZeroToOne() const override;
+ QMatrix4x4 clipSpaceCorrMatrix() const override;
+ bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const override;
+ bool isFeatureSupported(QRhi::Feature feature) const override;
+ int resourceLimit(QRhi::ResourceLimit limit) const override;
+ const QRhiNativeHandles *nativeHandles() override;
+ void sendVMemStatsToProfiler() override;
+ void makeThreadLocalNativeContextCurrent() override;
+
+ VkResult createDescriptorPool(VkDescriptorPool *pool);
+ bool allocateDescriptorSet(VkDescriptorSetAllocateInfo *allocInfo, VkDescriptorSet *result, int *resultPoolIndex);
+ uint32_t chooseTransientImageMemType(VkImage img, uint32_t startIndex);
+ bool createTransientImage(VkFormat format, const QSize &pixelSize, VkImageUsageFlags usage,
+ VkImageAspectFlags aspectMask, VkSampleCountFlagBits samples,
+ VkDeviceMemory *mem, VkImage *images, VkImageView *views, int count);
+
+ bool recreateSwapChain(QRhiSwapChain *swapChain);
+ void releaseSwapChainResources(QRhiSwapChain *swapChain);
+
+ VkFormat optimalDepthStencilFormat();
+ VkSampleCountFlagBits effectiveSampleCount(int sampleCount);
+ bool createDefaultRenderPass(VkRenderPass *rp,
+ bool hasDepthStencil,
+ VkSampleCountFlagBits samples,
+ VkFormat colorFormat);
+ bool createOffscreenRenderPass(VkRenderPass *rp,
+ const QVector<QRhiColorAttachment> &colorAttachments,
+ bool preserveColor,
+ bool preserveDs,
+ QRhiRenderBuffer *depthStencilBuffer,
+ QRhiTexture *depthTexture);
+ bool ensurePipelineCache();
+ VkShaderModule createShader(const QByteArray &spirv);
+
+ void prepareNewFrame(QRhiCommandBuffer *cb);
+ QRhi::FrameOpResult startCommandBuffer(VkCommandBuffer *cb);
+ QRhi::FrameOpResult endAndSubmitCommandBuffer(VkCommandBuffer cb, VkFence cmdFence,
+ VkSemaphore *waitSem, VkSemaphore *signalSem);
+ void waitCommandCompletion(int frameSlot);
+ VkDeviceSize subresUploadByteSize(const QRhiTextureSubresourceUploadDescription &subresDesc) const;
+ using BufferImageCopyList = QVarLengthArray<VkBufferImageCopy, 16>;
+ void prepareUploadSubres(QVkTexture *texD, int layer, int level,
+ const QRhiTextureSubresourceUploadDescription &subresDesc,
+ size_t *curOfs, void *mp,
+ BufferImageCopyList *copyInfos);
+ void enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates);
+ void executeBufferHostWritesForCurrentFrame(QVkBuffer *bufD);
+ void enqueueTransitionPassResources(QVkCommandBuffer *cbD);
+ void recordCommandBuffer(QVkCommandBuffer *cbD);
+ void trackedRegisterBuffer(QRhiPassResourceTracker *passResTracker,
+ QVkBuffer *bufD,
+ int slot,
+ QRhiPassResourceTracker::BufferAccess access,
+ QRhiPassResourceTracker::BufferStage stage);
+ void trackedRegisterTexture(QRhiPassResourceTracker *passResTracker,
+ QVkTexture *texD,
+ QRhiPassResourceTracker::TextureAccess access,
+ QRhiPassResourceTracker::TextureStage stage);
+ void recordTransitionPassResources(QVkCommandBuffer *cbD, const QRhiPassResourceTracker &tracker);
+ void activateTextureRenderTarget(QVkCommandBuffer *cbD, QVkTextureRenderTarget *rtD);
+ void executeDeferredReleases(bool forced = false);
+ void finishActiveReadbacks(bool forced = false);
+
+ void setObjectName(uint64_t object, VkDebugReportObjectTypeEXT type, const QByteArray &name, int slot = -1);
+ void trackedBufferBarrier(QVkCommandBuffer *cbD, QVkBuffer *bufD, int slot,
+ VkAccessFlags access, VkPipelineStageFlags stage);
+ void trackedImageBarrier(QVkCommandBuffer *cbD, QVkTexture *texD,
+ VkImageLayout layout, VkAccessFlags access, VkPipelineStageFlags stage);
+ void subresourceBarrier(QVkCommandBuffer *cbD, VkImage image,
+ VkImageLayout oldLayout, VkImageLayout newLayout,
+ VkAccessFlags srcAccess, VkAccessFlags dstAccess,
+ VkPipelineStageFlags srcStage, VkPipelineStageFlags dstStage,
+ int startLayer, int layerCount,
+ int startLevel, int levelCount);
+ void updateShaderResourceBindings(QRhiShaderResourceBindings *srb, int descSetIdx = -1);
+
+ QVulkanInstance *inst = nullptr;
+ QWindow *maybeWindow = nullptr;
+ bool importedDevice = false;
+ VkPhysicalDevice physDev = VK_NULL_HANDLE;
+ VkDevice dev = VK_NULL_HANDLE;
+ bool importedCmdPool = false;
+ VkCommandPool cmdPool = VK_NULL_HANDLE;
+ int gfxQueueFamilyIdx = -1;
+ VkQueue gfxQueue = VK_NULL_HANDLE;
+ bool hasCompute = false;
+ quint32 timestampValidBits = 0;
+ bool importedAllocator = false;
+ QVkAllocator allocator = nullptr;
+ QVulkanFunctions *f = nullptr;
+ QVulkanDeviceFunctions *df = nullptr;
+ VkPhysicalDeviceProperties physDevProperties;
+ VkDeviceSize ubufAlign;
+ VkDeviceSize texbufAlign;
+
+ bool debugMarkersAvailable = false;
+ bool vertexAttribDivisorAvailable = false;
+ PFN_vkCmdDebugMarkerBeginEXT vkCmdDebugMarkerBegin = nullptr;
+ PFN_vkCmdDebugMarkerEndEXT vkCmdDebugMarkerEnd = nullptr;
+ PFN_vkCmdDebugMarkerInsertEXT vkCmdDebugMarkerInsert = nullptr;
+ PFN_vkDebugMarkerSetObjectNameEXT vkDebugMarkerSetObjectName = nullptr;
+
+ PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR = nullptr;
+ PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR;
+ PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR;
+ PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR;
+ PFN_vkQueuePresentKHR vkQueuePresentKHR;
+ PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR = nullptr;
+ PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR;
+ PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR;
+
+ VkPipelineCache pipelineCache = VK_NULL_HANDLE;
+ struct DescriptorPoolData {
+ DescriptorPoolData() { }
+ DescriptorPoolData(VkDescriptorPool pool_)
+ : pool(pool_)
+ { }
+ VkDescriptorPool pool = VK_NULL_HANDLE;
+ int refCount = 0;
+ int allocedDescSets = 0;
+ };
+ QVector<DescriptorPoolData> descriptorPools;
+
+ VkQueryPool timestampQueryPool = VK_NULL_HANDLE;
+ QBitArray timestampQueryPoolMap;
+
+ VkFormat optimalDsFormat = VK_FORMAT_UNDEFINED;
+ QMatrix4x4 clipCorrectMatrix;
+
+ QVkSwapChain *currentSwapChain = nullptr;
+ QSet<QVkSwapChain *> swapchains;
+ QRhiVulkanNativeHandles nativeHandlesStruct;
+
+ struct OffscreenFrame {
+ OffscreenFrame(QRhiImplementation *rhi) : cbWrapper(rhi) { }
+ bool active = false;
+ QVkCommandBuffer cbWrapper;
+ VkFence cmdFence = VK_NULL_HANDLE;
+ } ofr;
+
+ struct ActiveReadback {
+ int activeFrameSlot = -1;
+ QRhiReadbackDescription desc;
+ QRhiReadbackResult *result;
+ VkBuffer buf;
+ QVkAlloc bufAlloc;
+ quint32 bufSize;
+ QSize pixelSize;
+ QRhiTexture::Format format;
+ };
+ QVector<ActiveReadback> activeReadbacks;
+
+ struct DeferredReleaseEntry {
+ enum Type {
+ Pipeline,
+ ShaderResourceBindings,
+ Buffer,
+ RenderBuffer,
+ Texture,
+ Sampler,
+ TextureRenderTarget,
+ RenderPass,
+ StagingBuffer
+ };
+ Type type;
+ int lastActiveFrameSlot; // -1 if not used otherwise 0..FRAMES_IN_FLIGHT-1
+ union {
+ struct {
+ VkPipeline pipeline;
+ VkPipelineLayout layout;
+ } pipelineState;
+ struct {
+ int poolIndex;
+ VkDescriptorSetLayout layout;
+ } shaderResourceBindings;
+ struct {
+ VkBuffer buffers[QVK_FRAMES_IN_FLIGHT];
+ QVkAlloc allocations[QVK_FRAMES_IN_FLIGHT];
+ VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT];
+ QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT];
+ } buffer;
+ struct {
+ VkDeviceMemory memory;
+ VkImage image;
+ VkImageView imageView;
+ } renderBuffer;
+ struct {
+ VkImage image;
+ VkImageView imageView;
+ QVkAlloc allocation;
+ VkBuffer stagingBuffers[QVK_FRAMES_IN_FLIGHT];
+ QVkAlloc stagingAllocations[QVK_FRAMES_IN_FLIGHT];
+ VkImageView extraImageViews[QRhi::MAX_LEVELS];
+ } texture;
+ struct {
+ VkSampler sampler;
+ } sampler;
+ struct {
+ VkFramebuffer fb;
+ VkImageView rtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
+ VkImageView resrtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
+ } textureRenderTarget;
+ struct {
+ VkRenderPass rp;
+ } renderPass;
+ struct {
+ VkBuffer stagingBuffer;
+ QVkAlloc stagingAllocation;
+ } stagingBuffer;
+ };
+ };
+ QVector<DeferredReleaseEntry> releaseQueue;
+};
+
+Q_DECLARE_TYPEINFO(QRhiVulkan::DescriptorPoolData, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QRhiVulkan::DeferredReleaseEntry, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QRhiVulkan::ActiveReadback, Q_MOVABLE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qrhivulkanext_p.h b/src/gui/rhi/qrhivulkanext_p.h
new file mode 100644
index 0000000000..67a63e07e0
--- /dev/null
+++ b/src/gui/rhi/qrhivulkanext_p.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt RHI module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRHIVULKANEXT_P_H
+#define QRHIVULKANEXT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qrhivulkan_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef VK_EXT_vertex_attribute_divisor
+#define VK_EXT_vertex_attribute_divisor 1
+#define VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_SPEC_VERSION 2
+#define VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME "VK_EXT_vertex_attribute_divisor"
+
+typedef struct VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT {
+ VkStructureType sType;
+ void* pNext;
+ uint32_t maxVertexAttribDivisor;
+} VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT;
+
+typedef struct VkVertexInputBindingDivisorDescriptionEXT {
+ uint32_t binding;
+ uint32_t divisor;
+} VkVertexInputBindingDivisorDescriptionEXT;
+
+typedef struct VkPipelineVertexInputDivisorStateCreateInfoEXT {
+ VkStructureType sType;
+ const void* pNext;
+ uint32_t vertexBindingDivisorCount;
+ const VkVertexInputBindingDivisorDescriptionEXT* pVertexBindingDivisors;
+} VkPipelineVertexInputDivisorStateCreateInfoEXT;
+#endif // VK_EXT_vertex_attribute_divisor
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qshader.cpp b/src/gui/rhi/qshader.cpp
new file mode 100644
index 0000000000..4676ec3f5b
--- /dev/null
+++ b/src/gui/rhi/qshader.cpp
@@ -0,0 +1,585 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qshader_p_p.h"
+#include <QDataStream>
+#include <QBuffer>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QShader
+ \inmodule QtRhi
+
+ \brief Contains multiple versions of a shader translated to multiple shading languages,
+ together with reflection metadata.
+
+ QShader is the entry point to shader code in the graphics API agnostic
+ Qt world. Instead of using GLSL shader sources, as was the custom with Qt
+ 5.x, new graphics systems with backends for multiple graphics APIs, such
+ as, Vulkan, Metal, Direct3D, and OpenGL, take QShader as their input
+ whenever a shader needs to be specified.
+
+ A QShader instance is empty and thus invalid by default. To get a useful
+ instance, the two typical methods are:
+
+ \list
+
+ \li Generate the contents offline, during build time or earlier, using the
+ \c qsb command line tool. The result is a binary file that is shipped with
+ the application, read via QIODevice::readAll(), and then deserialized via
+ fromSerialized(). For more information, see QShaderBaker.
+
+ \li Generate at run time via QShaderBaker. This is an expensive operation,
+ but allows applications to use user-provided or dynamically generated
+ shader source strings.
+
+ \endlist
+
+ When used together with the Qt Rendering Hardware Interface and its
+ classes, like QRhiGraphicsPipeline, no further action is needed from the
+ application's side as these classes are prepared to consume a QShader
+ whenever a shader needs to be specified for a given stage of the graphics
+ pipeline.
+
+ Alternatively, applications can access
+
+ \list
+
+ \li the source or byte code for any of the shading language versions that
+ are included in the QShader,
+
+ \li the name of the entry point for the shader,
+
+ \li the reflection metadata containing a description of the shader's
+ inputs, outputs and resources like uniform blocks. This is essential when
+ an application or framework needs to discover the inputs of a shader at
+ runtime due to not having advance knowledge of the vertex attributes or the
+ layout of the uniform buffers used by the shader.
+
+ \endlist
+
+ QShader makes no assumption about the shading language that was used
+ as the source for generating the various versions and variants that are
+ included in it.
+
+ QShader uses implicit sharing similarly to many core Qt types, and so
+ can be returned or passed by value. Detach happens implicitly when calling
+ a setter.
+
+ For reference, QRhi expects that a QShader suitable for all its
+ backends contains at least the following:
+
+ \list
+
+ \li SPIR-V 1.0 bytecode suitable for Vulkan 1.0 or newer
+
+ \li GLSL/ES 100 source code suitable for OpenGL ES 2.0 or newer
+
+ \li GLSL 120 source code suitable for OpenGL 2.1
+
+ \li HLSL Shader Model 5.0 source code or the corresponding DXBC bytecode suitable for Direct3D 11
+
+ \li Metal Shading Language 1.2 source code or the corresponding bytecode suitable for Metal
+
+ \endlist
+
+ \sa QShaderBaker
+ */
+
+/*!
+ \enum QShader::Stage
+ Describes the stage of the graphics pipeline the shader is suitable for.
+
+ \value VertexStage Vertex shader
+ \value TessellationControlStage Tessellation control (hull) shader
+ \value TessellationEvaluationStage Tessellation evaluation (domain) shader
+ \value GeometryStage Geometry shader
+ \value FragmentStage Fragment (pixel) shader
+ \value ComputeStage Compute shader
+ */
+
+/*!
+ \class QShaderVersion
+ \inmodule QtRhi
+
+ \brief Specifies the shading language version.
+
+ While languages like SPIR-V or the Metal Shading Language use traditional
+ version numbers, shaders for other APIs can use slightly different
+ versioning schemes. All those are mapped to a single version number in
+ here, however. For HLSL, the version refers to the Shader Model version,
+ like 5.0, 5.1, or 6.0. For GLSL an additional flag is needed to choose
+ between GLSL and GLSL/ES.
+
+ Below is a list with the most common examples of shader versions for
+ different graphics APIs:
+
+ \list
+
+ \li Vulkan (SPIR-V): 100
+ \li OpenGL: 120, 330, 440, etc.
+ \li OpenGL ES: 100 with GlslEs, 300 with GlslEs, etc.
+ \li Direct3D: 50, 51, 60
+ \li Metal: 12, 20
+ \endlist
+
+ A default constructed QShaderVersion contains a version of 100 and no
+ flags set.
+ */
+
+/*!
+ \enum QShaderVersion::Flag
+
+ Describes the flags that can be set.
+
+ \value GlslEs Indicates that GLSL/ES is meant in combination with GlslShader
+ */
+
+/*!
+ \class QShaderKey
+ \inmodule QtRhi
+
+ \brief Specifies the shading language, the version with flags, and the variant.
+
+ A default constructed QShaderKey has source set to SpirvShader and
+ sourceVersion set to 100. sourceVariant defaults to StandardShader.
+ */
+
+/*!
+ \enum QShader::Source
+ Describes what kind of shader code an entry contains.
+
+ \value SpirvShader SPIR-V
+ \value GlslShader GLSL
+ \value HlslShader HLSL
+ \value DxbcShader Direct3D bytecode (HLSL compiled by \c fxc)
+ \value MslShader Metal Shading Language
+ \value DxilShader Direct3D bytecode (HLSL compiled by \c dxc)
+ \value MetalLibShader Pre-compiled Metal bytecode
+ */
+
+/*!
+ \enum QShader::Variant
+ Describes what kind of shader code an entry contains.
+
+ \value StandardShader The normal, unmodified version of the shader code.
+ \value BatchableVertexShader Vertex shader rewritten to be suitable for Qt Quick scenegraph batching.
+ */
+
+/*!
+ \class QShaderCode
+ \inmodule QtRhi
+
+ \brief Contains source or binary code for a shader and additional metadata.
+
+ When shader() is empty after retrieving a QShaderCode instance from
+ QShader, it indicates no shader code was found for the requested key.
+ */
+
+static const int QSB_VERSION = 1;
+
+/*!
+ Constructs a new, empty (and thus invalid) QShader instance.
+ */
+QShader::QShader()
+ : d(new QShaderPrivate)
+{
+}
+
+/*!
+ \internal
+ */
+void QShader::detach()
+{
+ qAtomicDetach(d);
+}
+
+/*!
+ \internal
+ */
+QShader::QShader(const QShader &other)
+ : d(other.d)
+{
+ d->ref.ref();
+}
+
+/*!
+ \internal
+ */
+QShader &QShader::operator=(const QShader &other)
+{
+ qAtomicAssign(d, other.d);
+ return *this;
+}
+
+/*!
+ Destructor.
+ */
+QShader::~QShader()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+/*!
+ \return true if the QShader contains at least one shader version.
+ */
+bool QShader::isValid() const
+{
+ return !d->shaders.isEmpty();
+}
+
+/*!
+ \return the pipeline stage the shader is meant for.
+ */
+QShader::Stage QShader::stage() const
+{
+ return d->stage;
+}
+
+/*!
+ Sets the pipeline \a stage.
+ */
+void QShader::setStage(Stage stage)
+{
+ if (stage != d->stage) {
+ detach();
+ d->stage = stage;
+ }
+}
+
+/*!
+ \return the reflection metadata for the shader.
+ */
+QShaderDescription QShader::description() const
+{
+ return d->desc;
+}
+
+/*!
+ Sets the reflection metadata to \a desc.
+ */
+void QShader::setDescription(const QShaderDescription &desc)
+{
+ detach();
+ d->desc = desc;
+}
+
+/*!
+ \return the list of available shader versions
+ */
+QVector<QShaderKey> QShader::availableShaders() const
+{
+ return d->shaders.keys().toVector();
+}
+
+/*!
+ \return the source or binary code for a given shader version specified by \a key.
+ */
+QShaderCode QShader::shader(const QShaderKey &key) const
+{
+ return d->shaders.value(key);
+}
+
+/*!
+ Stores the source or binary \a shader code for a given shader version specified by \a key.
+ */
+void QShader::setShader(const QShaderKey &key, const QShaderCode &shader)
+{
+ if (d->shaders.value(key) == shader)
+ return;
+
+ detach();
+ d->shaders[key] = shader;
+}
+
+/*!
+ Removes the source or binary shader code for a given \a key.
+ Does nothing when not found.
+ */
+void QShader::removeShader(const QShaderKey &key)
+{
+ auto it = d->shaders.find(key);
+ if (it == d->shaders.end())
+ return;
+
+ detach();
+ d->shaders.erase(it);
+}
+
+/*!
+ \return a serialized binary version of all the data held by the
+ QShader, suitable for writing to files or other I/O devices.
+
+ \sa fromSerialized()
+ */
+QByteArray QShader::serialized() const
+{
+ QBuffer buf;
+ QDataStream ds(&buf);
+ ds.setVersion(QDataStream::Qt_5_10);
+ if (!buf.open(QIODevice::WriteOnly))
+ return QByteArray();
+
+ ds << QSB_VERSION;
+ ds << d->stage;
+ ds << d->desc.toBinaryJson();
+ ds << d->shaders.count();
+ for (auto it = d->shaders.cbegin(), itEnd = d->shaders.cend(); it != itEnd; ++it) {
+ const QShaderKey &k(it.key());
+ ds << k.source();
+ ds << k.sourceVersion().version();
+ ds << k.sourceVersion().flags();
+ ds << k.sourceVariant();
+ const QShaderCode &shader(d->shaders.value(k));
+ ds << shader.shader();
+ ds << shader.entryPoint();
+ }
+
+ return qCompress(buf.buffer());
+}
+
+/*!
+ Creates a new QShader instance from the given \a data.
+
+ \sa serialized()
+ */
+QShader QShader::fromSerialized(const QByteArray &data)
+{
+ QByteArray udata = qUncompress(data);
+ QBuffer buf(&udata);
+ QDataStream ds(&buf);
+ ds.setVersion(QDataStream::Qt_5_10);
+ if (!buf.open(QIODevice::ReadOnly))
+ return QShader();
+
+ QShader bs;
+ QShaderPrivate *d = QShaderPrivate::get(&bs);
+ Q_ASSERT(d->ref.load() == 1); // must be detached
+ int intVal;
+ ds >> intVal;
+ if (intVal != QSB_VERSION)
+ return QShader();
+
+ ds >> intVal;
+ d->stage = Stage(intVal);
+ QByteArray descBin;
+ ds >> descBin;
+ d->desc = QShaderDescription::fromBinaryJson(descBin);
+ int count;
+ ds >> count;
+ for (int i = 0; i < count; ++i) {
+ QShaderKey k;
+ ds >> intVal;
+ k.setSource(Source(intVal));
+ QShaderVersion ver;
+ ds >> intVal;
+ ver.setVersion(intVal);
+ ds >> intVal;
+ ver.setFlags(QShaderVersion::Flags(intVal));
+ k.setSourceVersion(ver);
+ ds >> intVal;
+ k.setSourceVariant(Variant(intVal));
+ QShaderCode shader;
+ QByteArray s;
+ ds >> s;
+ shader.setShader(s);
+ ds >> s;
+ shader.setEntryPoint(s);
+ d->shaders[k] = shader;
+ }
+
+ return bs;
+}
+
+QShaderVersion::QShaderVersion(int v, Flags f)
+ : m_version(v), m_flags(f)
+{
+}
+
+QShaderCode::QShaderCode(const QByteArray &code, const QByteArray &entry)
+ : m_shader(code), m_entryPoint(entry)
+{
+}
+
+QShaderKey::QShaderKey(QShader::Source s,
+ const QShaderVersion &sver,
+ QShader::Variant svar)
+ : m_source(s),
+ m_sourceVersion(sver),
+ m_sourceVariant(svar)
+{
+}
+
+/*!
+ Returns \c true if the two QShader objects \a a and \a b are equal,
+ meaning they are for the same stage with matching sets of shader source or
+ binary code.
+
+ \relates QShader
+ */
+bool operator==(const QShader &lhs, const QShader &rhs) Q_DECL_NOTHROW
+{
+ return lhs.d->stage == rhs.d->stage
+ && lhs.d->shaders == rhs.d->shaders;
+ // do not bother with desc, if the shader code is the same, the description must match too
+}
+
+/*!
+ \fn bool operator!=(const QShader &lhs, const QShader &rhs)
+
+ Returns \c false if the values in the two QShader objects \a a and \a b
+ are equal; otherwise returns \c true.
+
+ \relates QShader
+ */
+
+/*!
+ Returns the hash value for \a s, using \a seed to seed the calculation.
+
+ \relates QShader
+ */
+uint qHash(const QShader &s, uint seed) Q_DECL_NOTHROW
+{
+ uint h = s.stage();
+ for (auto it = s.d->shaders.constBegin(), itEnd = s.d->shaders.constEnd(); it != itEnd; ++it)
+ h += qHash(it.key(), seed) + qHash(it.value().shader(), seed);
+ return h;
+}
+
+/*!
+ Returns \c true if the two QShaderVersion objects \a a and \a b are
+ equal.
+
+ \relates QShaderVersion
+ */
+bool operator==(const QShaderVersion &lhs, const QShaderVersion &rhs) Q_DECL_NOTHROW
+{
+ return lhs.version() == rhs.version() && lhs.flags() == rhs.flags();
+}
+
+/*!
+ \fn bool operator!=(const QShaderVersion &lhs, const QShaderVersion &rhs)
+
+ Returns \c false if the values in the two QShaderVersion objects \a a
+ and \a b are equal; otherwise returns \c true.
+
+ \relates QShaderVersion
+ */
+
+/*!
+ Returns \c true if the two QShaderKey objects \a a and \a b are equal.
+
+ \relates QShaderKey
+ */
+bool operator==(const QShaderKey &lhs, const QShaderKey &rhs) Q_DECL_NOTHROW
+{
+ return lhs.source() == rhs.source() && lhs.sourceVersion() == rhs.sourceVersion()
+ && lhs.sourceVariant() == rhs.sourceVariant();
+}
+
+/*!
+ \fn bool operator!=(const QShaderKey &lhs, const QShaderKey &rhs)
+
+ Returns \c false if the values in the two QShaderKey objects \a a
+ and \a b are equal; otherwise returns \c true.
+
+ \relates QShaderKey
+ */
+
+/*!
+ Returns the hash value for \a k, using \a seed to seed the calculation.
+
+ \relates QShaderKey
+ */
+uint qHash(const QShaderKey &k, uint seed) Q_DECL_NOTHROW
+{
+ return seed + 10 * k.source() + k.sourceVersion().version() + k.sourceVersion().flags() + k.sourceVariant();
+}
+
+/*!
+ Returns \c true if the two QShaderCode objects \a a and \a b are equal.
+
+ \relates QShaderCode
+ */
+bool operator==(const QShaderCode &lhs, const QShaderCode &rhs) Q_DECL_NOTHROW
+{
+ return lhs.shader() == rhs.shader() && lhs.entryPoint() == rhs.entryPoint();
+}
+
+/*!
+ \fn bool operator!=(const QShaderCode &lhs, const QShaderCode &rhs)
+
+ Returns \c false if the values in the two QShaderCode objects \a a
+ and \a b are equal; otherwise returns \c true.
+
+ \relates QShaderCode
+ */
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QShader &bs)
+{
+ const QShaderPrivate *d = bs.d;
+ QDebugStateSaver saver(dbg);
+
+ dbg.nospace() << "QShader("
+ << "stage=" << d->stage
+ << " shaders=" << d->shaders.keys()
+ << " desc.isValid=" << d->desc.isValid()
+ << ')';
+
+ return dbg;
+}
+
+QDebug operator<<(QDebug dbg, const QShaderKey &k)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "ShaderKey(" << k.source()
+ << " " << k.sourceVersion()
+ << " " << k.sourceVariant() << ")";
+ return dbg;
+}
+
+QDebug operator<<(QDebug dbg, const QShaderVersion &v)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "Version(" << v.version() << " " << v.flags() << ")";
+ return dbg;
+}
+#endif // QT_NO_DEBUG_STREAM
+
+QT_END_NAMESPACE
diff --git a/src/gui/rhi/qshader_p.h b/src/gui/rhi/qshader_p.h
new file mode 100644
index 0000000000..243842a95a
--- /dev/null
+++ b/src/gui/rhi/qshader_p.h
@@ -0,0 +1,224 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSHADER_P_H
+#define QSHADER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/qtguiglobal.h>
+#include <private/qshaderdescription_p.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QShaderPrivate;
+class QShaderKey;
+
+class Q_GUI_EXPORT QShaderVersion
+{
+public:
+ enum Flag {
+ GlslEs = 0x01
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ QShaderVersion() = default;
+ QShaderVersion(int v, Flags f = Flags());
+
+ int version() const { return m_version; }
+ void setVersion(int v) { m_version = v; }
+
+ Flags flags() const { return m_flags; }
+ void setFlags(Flags f) { m_flags = f; }
+
+private:
+ int m_version = 100;
+ Flags m_flags;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QShaderVersion::Flags)
+Q_DECLARE_TYPEINFO(QShaderVersion, Q_MOVABLE_TYPE);
+
+class Q_GUI_EXPORT QShaderCode
+{
+public:
+ QShaderCode() = default;
+ QShaderCode(const QByteArray &code, const QByteArray &entry = QByteArray());
+
+ QByteArray shader() const { return m_shader; }
+ void setShader(const QByteArray &code) { m_shader = code; }
+
+ QByteArray entryPoint() const { return m_entryPoint; }
+ void setEntryPoint(const QByteArray &entry) { m_entryPoint = entry; }
+
+private:
+ QByteArray m_shader;
+ QByteArray m_entryPoint;
+};
+
+Q_DECLARE_TYPEINFO(QShaderCode, Q_MOVABLE_TYPE);
+
+class Q_GUI_EXPORT QShader
+{
+public:
+ enum Stage {
+ VertexStage = 0,
+ TessellationControlStage,
+ TessellationEvaluationStage,
+ GeometryStage,
+ FragmentStage,
+ ComputeStage
+ };
+
+ enum Source {
+ SpirvShader = 0,
+ GlslShader,
+ HlslShader,
+ DxbcShader, // fxc
+ MslShader,
+ DxilShader, // dxc
+ MetalLibShader // xcrun metal + xcrun metallib
+ };
+
+ enum Variant {
+ StandardShader = 0,
+ BatchableVertexShader
+ };
+
+ QShader();
+ QShader(const QShader &other);
+ QShader &operator=(const QShader &other);
+ ~QShader();
+ void detach();
+
+ bool isValid() const;
+
+ Stage stage() const;
+ void setStage(Stage stage);
+
+ QShaderDescription description() const;
+ void setDescription(const QShaderDescription &desc);
+
+ QVector<QShaderKey> availableShaders() const;
+ QShaderCode shader(const QShaderKey &key) const;
+ void setShader(const QShaderKey &key, const QShaderCode &shader);
+ void removeShader(const QShaderKey &key);
+
+ QByteArray serialized() const;
+ static QShader fromSerialized(const QByteArray &data);
+
+private:
+ QShaderPrivate *d;
+ friend struct QShaderPrivate;
+ friend Q_GUI_EXPORT bool operator==(const QShader &, const QShader &) Q_DECL_NOTHROW;
+ friend Q_GUI_EXPORT uint qHash(const QShader &, uint) Q_DECL_NOTHROW;
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_GUI_EXPORT QDebug operator<<(QDebug, const QShader &);
+#endif
+};
+
+class Q_GUI_EXPORT QShaderKey
+{
+public:
+ QShaderKey() = default;
+ QShaderKey(QShader::Source s,
+ const QShaderVersion &sver,
+ QShader::Variant svar = QShader::StandardShader);
+
+ QShader::Source source() const { return m_source; }
+ void setSource(QShader::Source s) { m_source = s; }
+
+ QShaderVersion sourceVersion() const { return m_sourceVersion; }
+ void setSourceVersion(const QShaderVersion &sver) { m_sourceVersion = sver; }
+
+ QShader::Variant sourceVariant() const { return m_sourceVariant; }
+ void setSourceVariant(QShader::Variant svar) { m_sourceVariant = svar; }
+
+private:
+ QShader::Source m_source = QShader::SpirvShader;
+ QShaderVersion m_sourceVersion;
+ QShader::Variant m_sourceVariant = QShader::StandardShader;
+};
+
+Q_DECLARE_TYPEINFO(QShaderKey, Q_MOVABLE_TYPE);
+
+Q_GUI_EXPORT bool operator==(const QShader &lhs, const QShader &rhs) Q_DECL_NOTHROW;
+Q_GUI_EXPORT uint qHash(const QShader &s, uint seed = 0) Q_DECL_NOTHROW;
+
+inline bool operator!=(const QShader &lhs, const QShader &rhs) Q_DECL_NOTHROW
+{
+ return !(lhs == rhs);
+}
+
+Q_GUI_EXPORT bool operator==(const QShaderVersion &lhs, const QShaderVersion &rhs) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator==(const QShaderKey &lhs, const QShaderKey &rhs) Q_DECL_NOTHROW;
+Q_GUI_EXPORT bool operator==(const QShaderCode &lhs, const QShaderCode &rhs) Q_DECL_NOTHROW;
+
+inline bool operator!=(const QShaderVersion &lhs, const QShaderVersion &rhs) Q_DECL_NOTHROW
+{
+ return !(lhs == rhs);
+}
+
+inline bool operator!=(const QShaderKey &lhs, const QShaderKey &rhs) Q_DECL_NOTHROW
+{
+ return !(lhs == rhs);
+}
+
+inline bool operator!=(const QShaderCode &lhs, const QShaderCode &rhs) Q_DECL_NOTHROW
+{
+ return !(lhs == rhs);
+}
+
+Q_GUI_EXPORT uint qHash(const QShaderKey &k, uint seed = 0) Q_DECL_NOTHROW;
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QShader &);
+Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QShaderKey &k);
+Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QShaderVersion &v);
+#endif
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qshader_p_p.h b/src/gui/rhi/qshader_p_p.h
new file mode 100644
index 0000000000..6473590e95
--- /dev/null
+++ b/src/gui/rhi/qshader_p_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSHADER_P_P_H
+#define QSHADER_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qshader_p.h"
+#include <QtCore/QAtomicInt>
+#include <QtCore/QHash>
+#include <QtCore/QDebug>
+
+QT_BEGIN_NAMESPACE
+
+struct Q_GUI_EXPORT QShaderPrivate
+{
+ QShaderPrivate()
+ : ref(1)
+ {
+ }
+
+ QShaderPrivate(const QShaderPrivate *other)
+ : ref(1),
+ stage(other->stage),
+ desc(other->desc),
+ shaders(other->shaders)
+ {
+ }
+
+ static QShaderPrivate *get(QShader *s) { return s->d; }
+ static const QShaderPrivate *get(const QShader *s) { return s->d; }
+
+ QAtomicInt ref;
+ QShader::Stage stage = QShader::VertexStage;
+ QShaderDescription desc;
+ QHash<QShaderKey, QShaderCode> shaders;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qshaderdescription.cpp b/src/gui/rhi/qshaderdescription.cpp
new file mode 100644
index 0000000000..77aceaddba
--- /dev/null
+++ b/src/gui/rhi/qshaderdescription.cpp
@@ -0,0 +1,1116 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qshaderdescription_p_p.h"
+#include <QDebug>
+#include <QJsonObject>
+#include <QJsonArray>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QShaderDescription
+ \inmodule QtRhi
+
+ \brief Describes the interface of a shader.
+
+ A shader typically has a set of inputs and outputs. A vertex shader for
+ example has a number of input variables and may use one or more uniform
+ buffers to access data (e.g. a modelview matrix) provided by the
+ application. The shader for the fragment stage receives data from the
+ vertex stage (in a simple setup) and may also rely on data from uniform
+ buffers, images, and samplers.
+
+ When it comes to vertex inputs and the layout of the uniform buffers (what
+ are the names of the members? what is there size, offset, and so on),
+ applications and frameworks may need to discover this dynamically at run
+ time. This is typical when the shader is not built-in but provided by an
+ external entity, like the user.
+
+ Modern and lean graphics APIs may no longer provide a way to query shader
+ reflection information at run time. Therefore, such data is now
+ automatically generated by QShaderBaker and is provided as a
+ QShaderDescription object for each and every QShader.
+
+ \section2 Example
+
+ Take the following vertex shader:
+
+ \badcode
+ #version 440
+
+ layout(location = 0) in vec4 position;
+ layout(location = 1) in vec3 color;
+ layout(location = 0) out vec3 v_color;
+
+ layout(std140, binding = 0) uniform buf {
+ mat4 mvp;
+ float opacity;
+ } ubuf;
+
+ out gl_PerVertex { vec4 gl_Position; };
+
+ void main()
+ {
+ v_color = color;
+ gl_Position = ubuf.mvp * position;
+ }
+ \endcode
+
+ This shader has two inputs: \c position at location 0 with a type of \c
+ vec4, and \c color at location 1 with a type of \c vec3. It has one output:
+ \c v_color, although this is typically not interesting for applications.
+ What is more important, there is a uniform block at binding 0 with a size
+ of 68 bytes and two members, a 4x4 matrix named \c mvp at offset 0, and a
+ float \c opacity at offset 64.
+
+ All this is described by a QShaderDescription object. QShaderDescription
+ can also be serialized to JSON and binary JSON, and can be deserialized
+ from binary JSON. In practice this is rarely needed since QShader
+ takes care of the associated QShaderDescription automatically, but if the
+ QShaderDescription of the above shader would be written out as JSON, it
+ would look like the following:
+
+ \badcode
+ {
+ "inputs": [
+ {
+ "location": 1,
+ "name": "color",
+ "type": "vec3"
+ },
+ {
+ "location": 0,
+ "name": "position",
+ "type": "vec4"
+ }
+ ],
+ "outputs": [
+ {
+ "location": 0,
+ "name": "v_color",
+ "type": "vec3"
+ }
+ ],
+ "uniformBlocks": [
+ {
+ "binding": 0,
+ "blockName": "buf",
+ "members": [
+ {
+ "matrixStride": 16,
+ "name": "mvp",
+ "offset": 0,
+ "size": 64,
+ "type": "mat4"
+ },
+ {
+ "name": "opacity",
+ "offset": 64,
+ "size": 4,
+ "type": "float"
+ }
+ ],
+ "set": 0,
+ "size": 68,
+ "structName": "ubuf"
+ }
+ ]
+ }
+ \endcode
+
+ The C++ API allows accessing a data structure like the above. For
+ simplicity the inner structs only contain public data members, also
+ considering that their layout is unlikely to change in the future.
+
+ \sa QShaderBaker, QShader
+ */
+
+/*!
+ \enum QShaderDescription::VariableType
+ Represents the type of a variable or block member.
+
+ \value Unknown
+ \value Float
+ \value Vec2
+ \value Vec3
+ \value Vec4
+ \value Mat2
+ \value Mat2x3
+ \value Mat2x4
+ \value Mat3
+ \value Mat3x2
+ \value Mat3x4
+ \value Mat4
+ \value Mat4x2
+ \value Mat4x3
+ \value Int
+ \value Int2
+ \value Int3
+ \value Int4
+ \value Uint
+ \value Uint2
+ \value Uint3
+ \value Uint4
+ \value Bool
+ \value Bool2
+ \value Bool3
+ \value Bool4
+ \value Double
+ \value Double2
+ \value Double3
+ \value Double4
+ \value DMat2
+ \value DMat2x3
+ \value DMat2x4
+ \value DMat3
+ \value DMat3x2
+ \value DMat3x4
+ \value DMat4
+ \value DMat4x2
+ \value DMat4x3
+ \value Sampler1D
+ \value Sampler2D
+ \value Sampler2DMS
+ \value Sampler3D
+ \value SamplerCube
+ \value Sampler1DArray
+ \value Sampler2DArray
+ \value Sampler2DMSArray
+ \value Sampler3DArray
+ \value SamplerCubeArray
+ \value SamplerRect
+ \value SamplerBuffer
+ \value Image1D
+ \value Image2D
+ \value Image2DMS
+ \value Image3D
+ \value ImageCube
+ \value Image1DArray
+ \value Image2DArray
+ \value Image2DMSArray
+ \value Image3DArray
+ \value ImageCubeArray
+ \value ImageRect
+ \value ImageBuffer
+ \value Struct
+ */
+
+/*!
+ \class QShaderDescription::InOutVariable
+ \inmodule QtRhi
+
+ \brief Describes an input or output variable in the shader.
+ */
+
+/*!
+ \class QShaderDescription::BlockVariable
+ \inmodule QtRhi
+
+ \brief Describes a member of a uniform or push constant block.
+ */
+
+/*!
+ \class QShaderDescription::UniformBlock
+ \inmodule QtRhi
+
+ \brief Describes a uniform block.
+
+ \note When translating to shading languages without uniform block support
+ (like GLSL 120 or GLSL/ES 100), uniform blocks are replaced with ordinary
+ uniforms in a struct. The name of the struct, and so the prefix for the
+ uniforms generated from the block members, is given by structName.
+ */
+
+/*!
+ \class QShaderDescription::PushConstantBlock
+ \inmodule QtRhi
+
+ \brief Describes a push constant block.
+ */
+
+/*!
+ \class QShaderDescription::StorageBlock
+ \inmodule QtRhi
+
+ \brief Describes a shader storage block.
+ */
+
+/*!
+ Constructs a new, empty QShaderDescription.
+
+ \note Being empty implies that isValid() returns \c false for the
+ newly constructed instance.
+ */
+QShaderDescription::QShaderDescription()
+ : d(new QShaderDescriptionPrivate)
+{
+}
+
+/*!
+ \internal
+ */
+void QShaderDescription::detach()
+{
+ qAtomicDetach(d);
+}
+
+/*!
+ \internal
+ */
+QShaderDescription::QShaderDescription(const QShaderDescription &other)
+ : d(other.d)
+{
+ d->ref.ref();
+}
+
+/*!
+ \internal
+ */
+QShaderDescription &QShaderDescription::operator=(const QShaderDescription &other)
+{
+ qAtomicAssign(d, other.d);
+ return *this;
+}
+
+/*!
+ Destructor.
+ */
+QShaderDescription::~QShaderDescription()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+/*!
+ \return true if the QShaderDescription contains at least one entry in one of
+ the variable and block lists.
+ */
+bool QShaderDescription::isValid() const
+{
+ return !d->inVars.isEmpty() || !d->outVars.isEmpty()
+ || !d->uniformBlocks.isEmpty() || !d->pushConstantBlocks.isEmpty() || !d->storageBlocks.isEmpty()
+ || !d->combinedImageSamplers.isEmpty() || !d->storageImages.isEmpty();
+}
+
+/*!
+ \return a serialized binary version of the data.
+
+ \sa toJson()
+ */
+QByteArray QShaderDescription::toBinaryJson() const
+{
+ return d->makeDoc().toBinaryData();
+}
+
+/*!
+ \return a serialized JSON text version of the data.
+
+ \note There is no deserialization method provided for JSON text.
+
+ \sa toBinaryJson()
+ */
+QByteArray QShaderDescription::toJson() const
+{
+ return d->makeDoc().toJson();
+}
+
+/*!
+ Deserializes the given binary JSON \a data and returns a new
+ QShaderDescription.
+ */
+QShaderDescription QShaderDescription::fromBinaryJson(const QByteArray &data)
+{
+ QShaderDescription desc;
+ QShaderDescriptionPrivate::get(&desc)->loadDoc(QJsonDocument::fromBinaryData(data));
+ return desc;
+}
+
+/*!
+ \return the list of input variables. This includes vertex inputs (sometimes
+ called attributes) for the vertex stage, and inputs for other stages
+ (sometimes called varyings).
+ */
+QVector<QShaderDescription::InOutVariable> QShaderDescription::inputVariables() const
+{
+ return d->inVars;
+}
+
+/*!
+ \return the list of output variables.
+ */
+QVector<QShaderDescription::InOutVariable> QShaderDescription::outputVariables() const
+{
+ return d->outVars;
+}
+
+/*!
+ \return the list of uniform blocks.
+ */
+QVector<QShaderDescription::UniformBlock> QShaderDescription::uniformBlocks() const
+{
+ return d->uniformBlocks;
+}
+
+/*!
+ \return the list of push constant blocks.
+
+ \note Avoid relying on push constant blocks for shaders that are to be used
+ in combination with the Qt Rendering Hardware Interface since that
+ currently has no support for them.
+ */
+QVector<QShaderDescription::PushConstantBlock> QShaderDescription::pushConstantBlocks() const
+{
+ return d->pushConstantBlocks;
+}
+
+/*!
+ \return the list of shader storage blocks.
+
+ For example, with GLSL/Vulkan shaders as source, the declaration
+
+ \badcode
+ struct Stuff {
+ vec2 a;
+ vec2 b;
+ };
+ layout(std140, binding = 0) buffer StuffSsbo {
+ vec4 whatever;
+ Stuff stuff[];
+ } buf;
+ \endcode
+
+ generates the following: (shown as textual JSON here)
+
+ \badcode
+ "storageBlocks": [ {
+ "binding": 0,
+ "blockName": "StuffSsbo",
+ "instanceName": "buf",
+ "knownSize": 16,
+ "members": [
+ {
+ "name": "whatever",
+ "offset": 0,
+ "size": 16,
+ "type": "vec4"
+ },
+ {
+ "arrayDims": [
+ 0
+ ],
+ "name": "stuff",
+ "offset": 16,
+ "size": 0,
+ "structMembers": [
+ {
+ "name": "a",
+ "offset": 0,
+ "size": 8,
+ "type": "vec2"
+ },
+ {
+ "name": "b",
+ "offset": 8,
+ "size": 8,
+ "type": "vec2"
+ }
+ ],
+ "type": "struct"
+ }
+ ],
+ "set": 0
+ } ]
+ \endcode
+
+ \note The size of the last member in the storage block is undefined. This shows
+ up as \c size 0 and an array dimension of \c{[0]}. The storage block's \c knownSize
+ excludes the size of the last member since that will only be known at run time.
+
+ \note SSBOs are not available with some graphics APIs, such as, OpenGL 2.x or
+ OpenGL ES older than 3.1.
+ */
+QVector<QShaderDescription::StorageBlock> QShaderDescription::storageBlocks() const
+{
+ return d->storageBlocks;
+}
+
+/*!
+ \return the list of combined image samplers
+
+ With GLSL/Vulkan shaders as source a \c{layout(binding = 1) uniform sampler2D tex;}
+ uniform generates the following: (shown as textual JSON here)
+
+ \badcode
+ "combinedImageSamplers": [
+ {
+ "binding": 1,
+ "name": "tex",
+ "set": 0,
+ "type": "sampler2D"
+ }
+ ]
+ \endcode
+
+ This does not mean that other language versions of the shader must also use
+ a combined image sampler, especially considering that the concept may not
+ exist everywhere. For instance, a HLSL version will likely just use a
+ Texture2D and SamplerState object with registers t1 and s1, respectively.
+ */
+QVector<QShaderDescription::InOutVariable> QShaderDescription::combinedImageSamplers() const
+{
+ return d->combinedImageSamplers;
+}
+
+/*!
+ \return the list of image variables.
+
+ These will likely occur in compute shaders. For example,
+ \c{layout (binding = 0, rgba8) uniform readonly image2D inputImage;}
+ generates the following: (shown as textual JSON here)
+
+ \badcode
+ "storageImages": [
+ {
+ "binding": 0,
+ "imageFormat": "rgba8",
+ "name": "inputImage",
+ "set": 0,
+ "type": "image2D"
+ }
+ ]
+ \endcode
+
+ \note Separate image objects are not compatible with some graphics APIs,
+ such as, OpenGL 2.x or OpenGL ES older than 3.1.
+ */
+QVector<QShaderDescription::InOutVariable> QShaderDescription::storageImages() const
+{
+ return d->storageImages;
+}
+
+/*!
+ Returns the local size of a compute shader.
+
+ For example, for a compute shader with the following declaration the
+ function returns { 256, 16, 1}.
+
+ \badcode
+ layout(local_size_x = 256, local_size_y = 16, local_size_z = 1) in;
+ \endcode
+ */
+std::array<uint, 3> QShaderDescription::computeShaderLocalSize() const
+{
+ return d->localSize;
+}
+
+static struct TypeTab {
+ QString k;
+ QShaderDescription::VariableType v;
+} typeTab[] = {
+ { QLatin1String("float"), QShaderDescription::Float },
+ { QLatin1String("vec2"), QShaderDescription::Vec2 },
+ { QLatin1String("vec3"), QShaderDescription::Vec3 },
+ { QLatin1String("vec4"), QShaderDescription::Vec4 },
+ { QLatin1String("mat2"), QShaderDescription::Mat2 },
+ { QLatin1String("mat3"), QShaderDescription::Mat3 },
+ { QLatin1String("mat4"), QShaderDescription::Mat4 },
+
+ { QLatin1String("struct"), QShaderDescription::Struct },
+
+ { QLatin1String("sampler1D"), QShaderDescription::Sampler1D },
+ { QLatin1String("sampler2D"), QShaderDescription::Sampler2D },
+ { QLatin1String("sampler2DMS"), QShaderDescription::Sampler2DMS },
+ { QLatin1String("sampler3D"), QShaderDescription::Sampler3D },
+ { QLatin1String("samplerCube"), QShaderDescription::SamplerCube },
+ { QLatin1String("sampler1DArray"), QShaderDescription::Sampler1DArray },
+ { QLatin1String("sampler2DArray"), QShaderDescription::Sampler2DArray },
+ { QLatin1String("sampler2DMSArray"), QShaderDescription::Sampler2DMSArray },
+ { QLatin1String("sampler3DArray"), QShaderDescription::Sampler3DArray },
+ { QLatin1String("samplerCubeArray"), QShaderDescription::SamplerCubeArray },
+ { QLatin1String("samplerRect"), QShaderDescription::SamplerRect },
+ { QLatin1String("samplerBuffer"), QShaderDescription::SamplerBuffer },
+
+ { QLatin1String("mat2x3"), QShaderDescription::Mat2x3 },
+ { QLatin1String("mat2x4"), QShaderDescription::Mat2x4 },
+ { QLatin1String("mat3x2"), QShaderDescription::Mat3x2 },
+ { QLatin1String("mat3x4"), QShaderDescription::Mat3x4 },
+ { QLatin1String("mat4x2"), QShaderDescription::Mat4x2 },
+ { QLatin1String("mat4x3"), QShaderDescription::Mat4x3 },
+
+ { QLatin1String("int"), QShaderDescription::Int },
+ { QLatin1String("ivec2"), QShaderDescription::Int2 },
+ { QLatin1String("ivec3"), QShaderDescription::Int3 },
+ { QLatin1String("ivec4"), QShaderDescription::Int4 },
+
+ { QLatin1String("uint"), QShaderDescription::Uint },
+ { QLatin1String("uvec2"), QShaderDescription::Uint2 },
+ { QLatin1String("uvec3"), QShaderDescription::Uint3 },
+ { QLatin1String("uvec4"), QShaderDescription::Uint4 },
+
+ { QLatin1String("bool"), QShaderDescription::Bool },
+ { QLatin1String("bvec2"), QShaderDescription::Bool2 },
+ { QLatin1String("bvec3"), QShaderDescription::Bool3 },
+ { QLatin1String("bvec4"), QShaderDescription::Bool4 },
+
+ { QLatin1String("double"), QShaderDescription::Double },
+ { QLatin1String("dvec2"), QShaderDescription::Double2 },
+ { QLatin1String("dvec3"), QShaderDescription::Double3 },
+ { QLatin1String("dvec4"), QShaderDescription::Double4 },
+ { QLatin1String("dmat2"), QShaderDescription::DMat2 },
+ { QLatin1String("dmat3"), QShaderDescription::DMat3 },
+ { QLatin1String("dmat4"), QShaderDescription::DMat4 },
+ { QLatin1String("dmat2x3"), QShaderDescription::DMat2x3 },
+ { QLatin1String("dmat2x4"), QShaderDescription::DMat2x4 },
+ { QLatin1String("dmat3x2"), QShaderDescription::DMat3x2 },
+ { QLatin1String("dmat3x4"), QShaderDescription::DMat3x4 },
+ { QLatin1String("dmat4x2"), QShaderDescription::DMat4x2 },
+ { QLatin1String("dmat4x3"), QShaderDescription::DMat4x3 },
+
+ { QLatin1String("image1D"), QShaderDescription::Image1D },
+ { QLatin1String("image2D"), QShaderDescription::Image2D },
+ { QLatin1String("image2DMS"), QShaderDescription::Image2DMS },
+ { QLatin1String("image3D"), QShaderDescription::Image3D },
+ { QLatin1String("imageCube"), QShaderDescription::ImageCube },
+ { QLatin1String("image1DArray"), QShaderDescription::Image1DArray },
+ { QLatin1String("image2DArray"), QShaderDescription::Image2DArray },
+ { QLatin1String("image2DMSArray"), QShaderDescription::Image2DMSArray },
+ { QLatin1String("image3DArray"), QShaderDescription::Image3DArray },
+ { QLatin1String("imageCubeArray"), QShaderDescription::ImageCubeArray },
+ { QLatin1String("imageRect"), QShaderDescription::ImageRect },
+ { QLatin1String("imageBuffer"), QShaderDescription::ImageBuffer }
+};
+
+static QString typeStr(const QShaderDescription::VariableType &t)
+{
+ for (size_t i = 0; i < sizeof(typeTab) / sizeof(TypeTab); ++i) {
+ if (typeTab[i].v == t)
+ return typeTab[i].k;
+ }
+ return QString();
+}
+
+static QShaderDescription::VariableType mapType(const QString &t)
+{
+ for (size_t i = 0; i < sizeof(typeTab) / sizeof(TypeTab); ++i) {
+ if (typeTab[i].k == t)
+ return typeTab[i].v;
+ }
+ return QShaderDescription::Unknown;
+}
+
+static struct ImageFormatTab {
+ QString k;
+ QShaderDescription::ImageFormat v;
+} imageFormatTab[] {
+ { QLatin1String("unknown"), QShaderDescription::ImageFormatUnknown },
+ { QLatin1String("rgba32f"), QShaderDescription::ImageFormatRgba32f },
+ { QLatin1String("rgba16"), QShaderDescription::ImageFormatRgba16f },
+ { QLatin1String("r32f"), QShaderDescription::ImageFormatR32f },
+ { QLatin1String("rgba8"), QShaderDescription::ImageFormatRgba8 },
+ { QLatin1String("rgba8_snorm"), QShaderDescription::ImageFormatRgba8Snorm },
+ { QLatin1String("rg32f"), QShaderDescription::ImageFormatRg32f },
+ { QLatin1String("rg16f"), QShaderDescription::ImageFormatRg16f },
+ { QLatin1String("r11f_g11f_b10f"), QShaderDescription::ImageFormatR11fG11fB10f },
+ { QLatin1String("r16f"), QShaderDescription::ImageFormatR16f },
+ { QLatin1String("rgba16"), QShaderDescription::ImageFormatRgba16 },
+ { QLatin1String("rgb10_a2"), QShaderDescription::ImageFormatRgb10A2 },
+ { QLatin1String("rg16"), QShaderDescription::ImageFormatRg16 },
+ { QLatin1String("rg8"), QShaderDescription::ImageFormatRg8 },
+ { QLatin1String("r16"), QShaderDescription::ImageFormatR16 },
+ { QLatin1String("r8"), QShaderDescription::ImageFormatR8 },
+ { QLatin1String("rgba16_snorm"), QShaderDescription::ImageFormatRgba16Snorm },
+ { QLatin1String("rg16_snorm"), QShaderDescription::ImageFormatRg16Snorm },
+ { QLatin1String("rg8_snorm"), QShaderDescription::ImageFormatRg8Snorm },
+ { QLatin1String("r16_snorm"), QShaderDescription::ImageFormatR16Snorm },
+ { QLatin1String("r8_snorm"), QShaderDescription::ImageFormatR8Snorm },
+ { QLatin1String("rgba32i"), QShaderDescription::ImageFormatRgba32i },
+ { QLatin1String("rgba16i"), QShaderDescription::ImageFormatRgba16i },
+ { QLatin1String("rgba8i"), QShaderDescription::ImageFormatRgba8i },
+ { QLatin1String("r32i"), QShaderDescription::ImageFormatR32i },
+ { QLatin1String("rg32i"), QShaderDescription::ImageFormatRg32i },
+ { QLatin1String("rg16i"), QShaderDescription::ImageFormatRg16i },
+ { QLatin1String("rg8i"), QShaderDescription::ImageFormatRg8i },
+ { QLatin1String("r16i"), QShaderDescription::ImageFormatR16i },
+ { QLatin1String("r8i"), QShaderDescription::ImageFormatR8i },
+ { QLatin1String("rgba32ui"), QShaderDescription::ImageFormatRgba32ui },
+ { QLatin1String("rgba16ui"), QShaderDescription::ImageFormatRgba16ui },
+ { QLatin1String("rgba8ui"), QShaderDescription::ImageFormatRgba8ui },
+ { QLatin1String("r32ui"), QShaderDescription::ImageFormatR32ui },
+ { QLatin1String("rgb10_a2ui"), QShaderDescription::ImageFormatRgb10a2ui },
+ { QLatin1String("rg32ui"), QShaderDescription::ImageFormatRg32ui },
+ { QLatin1String("rg16ui"), QShaderDescription::ImageFormatRg16ui },
+ { QLatin1String("rg8ui"), QShaderDescription::ImageFormatRg8ui },
+ { QLatin1String("r16ui"), QShaderDescription::ImageFormatR16ui },
+ { QLatin1String("r8ui"), QShaderDescription::ImageFormatR8ui }
+};
+
+static QString imageFormatStr(const QShaderDescription::ImageFormat &f)
+{
+ for (size_t i = 0; i < sizeof(imageFormatTab) / sizeof(ImageFormatTab); ++i) {
+ if (imageFormatTab[i].v == f)
+ return imageFormatTab[i].k;
+ }
+ return QString();
+}
+
+static QShaderDescription::ImageFormat mapImageFormat(const QString &f)
+{
+ for (size_t i = 0; i < sizeof(imageFormatTab) / sizeof(ImageFormatTab); ++i) {
+ if (imageFormatTab[i].k == f)
+ return imageFormatTab[i].v;
+ }
+ return QShaderDescription::ImageFormatUnknown;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QShaderDescription &sd)
+{
+ const QShaderDescriptionPrivate *d = sd.d;
+ QDebugStateSaver saver(dbg);
+
+ if (sd.isValid()) {
+ dbg.nospace() << "QShaderDescription("
+ << "inVars " << d->inVars
+ << " outVars " << d->outVars
+ << " uniformBlocks " << d->uniformBlocks
+ << " pcBlocks " << d->pushConstantBlocks
+ << " storageBlocks " << d->storageBlocks
+ << " combinedSamplers " << d->combinedImageSamplers
+ << " images " << d->storageImages
+ << ')';
+ } else {
+ dbg.nospace() << "QShaderDescription(null)";
+ }
+
+ return dbg;
+}
+
+QDebug operator<<(QDebug dbg, const QShaderDescription::InOutVariable &var)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "InOutVariable(" << typeStr(var.type) << ' ' << var.name;
+ if (var.location >= 0)
+ dbg.nospace() << " location=" << var.location;
+ if (var.binding >= 0)
+ dbg.nospace() << " binding=" << var.binding;
+ if (var.descriptorSet >= 0)
+ dbg.nospace() << " set=" << var.descriptorSet;
+ if (var.imageFormat != QShaderDescription::ImageFormatUnknown)
+ dbg.nospace() << " imageFormat=" << imageFormatStr(var.imageFormat);
+ if (var.imageFlags)
+ dbg.nospace() << " imageFlags=" << var.imageFlags;
+ dbg.nospace() << ')';
+ return dbg;
+}
+
+QDebug operator<<(QDebug dbg, const QShaderDescription::BlockVariable &var)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "BlockVariable(" << typeStr(var.type) << ' ' << var.name
+ << " offset=" << var.offset << " size=" << var.size;
+ if (!var.arrayDims.isEmpty())
+ dbg.nospace() << " array=" << var.arrayDims;
+ if (var.arrayStride)
+ dbg.nospace() << " arrayStride=" << var.arrayStride;
+ if (var.matrixStride)
+ dbg.nospace() << " matrixStride=" << var.matrixStride;
+ if (var.matrixIsRowMajor)
+ dbg.nospace() << " [rowmaj]";
+ if (!var.structMembers.isEmpty())
+ dbg.nospace() << " structMembers=" << var.structMembers;
+ dbg.nospace() << ')';
+ return dbg;
+}
+
+QDebug operator<<(QDebug dbg, const QShaderDescription::UniformBlock &blk)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "UniformBlock(" << blk.blockName << ' ' << blk.structName << " size=" << blk.size;
+ if (blk.binding >= 0)
+ dbg.nospace() << " binding=" << blk.binding;
+ if (blk.descriptorSet >= 0)
+ dbg.nospace() << " set=" << blk.descriptorSet;
+ dbg.nospace() << ' ' << blk.members << ')';
+ return dbg;
+}
+
+QDebug operator<<(QDebug dbg, const QShaderDescription::PushConstantBlock &blk)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "PushConstantBlock(" << blk.name << " size=" << blk.size << ' ' << blk.members << ')';
+ return dbg;
+}
+
+QDebug operator<<(QDebug dbg, const QShaderDescription::StorageBlock &blk)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace() << "StorageBlock(" << blk.blockName << ' ' << blk.instanceName << " knownSize=" << blk.knownSize;
+ if (blk.binding >= 0)
+ dbg.nospace() << " binding=" << blk.binding;
+ if (blk.descriptorSet >= 0)
+ dbg.nospace() << " set=" << blk.descriptorSet;
+ dbg.nospace() << ' ' << blk.members << ')';
+ return dbg;
+}
+#endif
+
+static const QString nameKey = QLatin1String("name");
+static const QString typeKey = QLatin1String("type");
+static const QString locationKey = QLatin1String("location");
+static const QString bindingKey = QLatin1String("binding");
+static const QString setKey = QLatin1String("set");
+static const QString imageFormatKey = QLatin1String("imageFormat");
+static const QString imageFlagsKey = QLatin1String("imageFlags");
+static const QString offsetKey = QLatin1String("offset");
+static const QString arrayDimsKey = QLatin1String("arrayDims");
+static const QString arrayStrideKey = QLatin1String("arrayStride");
+static const QString matrixStrideKey = QLatin1String("matrixStride");
+static const QString matrixRowMajorKey = QLatin1String("matrixRowMajor");
+static const QString structMembersKey = QLatin1String("structMembers");
+static const QString membersKey = QLatin1String("members");
+static const QString inputsKey = QLatin1String("inputs");
+static const QString outputsKey = QLatin1String("outputs");
+static const QString uniformBlocksKey = QLatin1String("uniformBlocks");
+static const QString blockNameKey = QLatin1String("blockName");
+static const QString structNameKey = QLatin1String("structName");
+static const QString instanceNameKey = QLatin1String("instanceName");
+static const QString sizeKey = QLatin1String("size");
+static const QString knownSizeKey = QLatin1String("knownSize");
+static const QString pushConstantBlocksKey = QLatin1String("pushConstantBlocks");
+static const QString storageBlocksKey = QLatin1String("storageBlocks");
+static const QString combinedImageSamplersKey = QLatin1String("combinedImageSamplers");
+static const QString storageImagesKey = QLatin1String("storageImages");
+static const QString localSizeKey = QLatin1String("localSize");
+
+static void addDeco(QJsonObject *obj, const QShaderDescription::InOutVariable &v)
+{
+ if (v.location >= 0)
+ (*obj)[locationKey] = v.location;
+ if (v.binding >= 0)
+ (*obj)[bindingKey] = v.binding;
+ if (v.descriptorSet >= 0)
+ (*obj)[setKey] = v.descriptorSet;
+ if (v.imageFormat != QShaderDescription::ImageFormatUnknown)
+ (*obj)[imageFormatKey] = imageFormatStr(v.imageFormat);
+ if (v.imageFlags)
+ (*obj)[imageFlagsKey] = int(v.imageFlags);
+}
+
+static QJsonObject inOutObject(const QShaderDescription::InOutVariable &v)
+{
+ QJsonObject obj;
+ obj[nameKey] = v.name;
+ obj[typeKey] = typeStr(v.type);
+ addDeco(&obj, v);
+ return obj;
+}
+
+static QJsonObject blockMemberObject(const QShaderDescription::BlockVariable &v)
+{
+ QJsonObject obj;
+ obj[nameKey] = v.name;
+ obj[typeKey] = typeStr(v.type);
+ obj[offsetKey] = v.offset;
+ obj[sizeKey] = v.size;
+ if (!v.arrayDims.isEmpty()) {
+ QJsonArray dimArr;
+ for (int dim : v.arrayDims)
+ dimArr.append(dim);
+ obj[arrayDimsKey] = dimArr;
+ }
+ if (v.arrayStride)
+ obj[arrayStrideKey] = v.arrayStride;
+ if (v.matrixStride)
+ obj[matrixStrideKey] = v.matrixStride;
+ if (v.matrixIsRowMajor)
+ obj[matrixRowMajorKey] = true;
+ if (!v.structMembers.isEmpty()) {
+ QJsonArray arr;
+ for (const QShaderDescription::BlockVariable &sv : v.structMembers)
+ arr.append(blockMemberObject(sv));
+ obj[structMembersKey] = arr;
+ }
+ return obj;
+}
+
+QJsonDocument QShaderDescriptionPrivate::makeDoc()
+{
+ QJsonObject root;
+
+ QJsonArray jinputs;
+ for (const QShaderDescription::InOutVariable &v : qAsConst(inVars))
+ jinputs.append(inOutObject(v));
+ if (!jinputs.isEmpty())
+ root[inputsKey] = jinputs;
+
+ QJsonArray joutputs;
+ for (const QShaderDescription::InOutVariable &v : qAsConst(outVars))
+ joutputs.append(inOutObject(v));
+ if (!joutputs.isEmpty())
+ root[outputsKey] = joutputs;
+
+ QJsonArray juniformBlocks;
+ for (const QShaderDescription::UniformBlock &b : uniformBlocks) {
+ QJsonObject juniformBlock;
+ juniformBlock[blockNameKey] = b.blockName;
+ juniformBlock[structNameKey] = b.structName;
+ juniformBlock[sizeKey] = b.size;
+ if (b.binding >= 0)
+ juniformBlock[bindingKey] = b.binding;
+ if (b.descriptorSet >= 0)
+ juniformBlock[setKey] = b.descriptorSet;
+ QJsonArray members;
+ for (const QShaderDescription::BlockVariable &v : b.members)
+ members.append(blockMemberObject(v));
+ juniformBlock[membersKey] = members;
+ juniformBlocks.append(juniformBlock);
+ }
+ if (!juniformBlocks.isEmpty())
+ root[uniformBlocksKey] = juniformBlocks;
+
+ QJsonArray jpushConstantBlocks;
+ for (const QShaderDescription::PushConstantBlock &b : pushConstantBlocks) {
+ QJsonObject jpushConstantBlock;
+ jpushConstantBlock[nameKey] = b.name;
+ jpushConstantBlock[sizeKey] = b.size;
+ QJsonArray members;
+ for (const QShaderDescription::BlockVariable &v : b.members)
+ members.append(blockMemberObject(v));
+ jpushConstantBlock[membersKey] = members;
+ jpushConstantBlocks.append(jpushConstantBlock);
+ }
+ if (!jpushConstantBlocks.isEmpty())
+ root[pushConstantBlocksKey] = jpushConstantBlocks;
+
+ QJsonArray jstorageBlocks;
+ for (const QShaderDescription::StorageBlock &b : storageBlocks) {
+ QJsonObject jstorageBlock;
+ jstorageBlock[blockNameKey] = b.blockName;
+ jstorageBlock[instanceNameKey] = b.instanceName;
+ jstorageBlock[knownSizeKey] = b.knownSize;
+ if (b.binding >= 0)
+ jstorageBlock[bindingKey] = b.binding;
+ if (b.descriptorSet >= 0)
+ jstorageBlock[setKey] = b.descriptorSet;
+ QJsonArray members;
+ for (const QShaderDescription::BlockVariable &v : b.members)
+ members.append(blockMemberObject(v));
+ jstorageBlock[membersKey] = members;
+ jstorageBlocks.append(jstorageBlock);
+ }
+ if (!jstorageBlocks.isEmpty())
+ root[storageBlocksKey] = jstorageBlocks;
+
+ QJsonArray jcombinedSamplers;
+ for (const QShaderDescription::InOutVariable &v : qAsConst(combinedImageSamplers)) {
+ QJsonObject sampler;
+ sampler[nameKey] = v.name;
+ sampler[typeKey] = typeStr(v.type);
+ addDeco(&sampler, v);
+ jcombinedSamplers.append(sampler);
+ }
+ if (!jcombinedSamplers.isEmpty())
+ root[combinedImageSamplersKey] = jcombinedSamplers;
+
+ QJsonArray jstorageImages;
+ for (const QShaderDescription::InOutVariable &v : qAsConst(storageImages)) {
+ QJsonObject image;
+ image[nameKey] = v.name;
+ image[typeKey] = typeStr(v.type);
+ addDeco(&image, v);
+ jstorageImages.append(image);
+ }
+ if (!jstorageImages.isEmpty())
+ root[storageImagesKey] = jstorageImages;
+
+ QJsonArray jlocalSize;
+ for (int i = 0; i < 3; ++i)
+ jlocalSize.append(QJsonValue(int(localSize[i])));
+ root[localSizeKey] = jlocalSize;
+
+ return QJsonDocument(root);
+}
+
+static QShaderDescription::InOutVariable inOutVar(const QJsonObject &obj)
+{
+ QShaderDescription::InOutVariable var;
+ var.name = obj[nameKey].toString();
+ var.type = mapType(obj[typeKey].toString());
+ if (obj.contains(locationKey))
+ var.location = obj[locationKey].toInt();
+ if (obj.contains(bindingKey))
+ var.binding = obj[bindingKey].toInt();
+ if (obj.contains(setKey))
+ var.descriptorSet = obj[setKey].toInt();
+ if (obj.contains(imageFormatKey))
+ var.imageFormat = mapImageFormat(obj[imageFormatKey].toString());
+ if (obj.contains(imageFlagsKey))
+ var.imageFlags = QShaderDescription::ImageFlags(obj[imageFlagsKey].toInt());
+ return var;
+}
+
+static QShaderDescription::BlockVariable blockVar(const QJsonObject &obj)
+{
+ QShaderDescription::BlockVariable var;
+ var.name = obj[nameKey].toString();
+ var.type = mapType(obj[typeKey].toString());
+ var.offset = obj[offsetKey].toInt();
+ var.size = obj[sizeKey].toInt();
+ if (obj.contains(arrayDimsKey)) {
+ QJsonArray dimArr = obj[arrayDimsKey].toArray();
+ for (int i = 0; i < dimArr.count(); ++i)
+ var.arrayDims.append(dimArr.at(i).toInt());
+ }
+ if (obj.contains(arrayStrideKey))
+ var.arrayStride = obj[arrayStrideKey].toInt();
+ if (obj.contains(matrixStrideKey))
+ var.matrixStride = obj[matrixStrideKey].toInt();
+ if (obj.contains(matrixRowMajorKey))
+ var.matrixIsRowMajor = obj[matrixRowMajorKey].toBool();
+ if (obj.contains(structMembersKey)) {
+ QJsonArray arr = obj[structMembersKey].toArray();
+ for (int i = 0; i < arr.count(); ++i)
+ var.structMembers.append(blockVar(arr.at(i).toObject()));
+ }
+ return var;
+}
+
+void QShaderDescriptionPrivate::loadDoc(const QJsonDocument &doc)
+{
+ if (doc.isNull()) {
+ qWarning("QShaderDescription: JSON document is empty");
+ return;
+ }
+
+ Q_ASSERT(ref.load() == 1); // must be detached
+
+ inVars.clear();
+ outVars.clear();
+ uniformBlocks.clear();
+ pushConstantBlocks.clear();
+ storageBlocks.clear();
+ combinedImageSamplers.clear();
+ storageImages.clear();
+
+ QJsonObject root = doc.object();
+
+ if (root.contains(inputsKey)) {
+ QJsonArray inputs = root[inputsKey].toArray();
+ for (int i = 0; i < inputs.count(); ++i)
+ inVars.append(inOutVar(inputs[i].toObject()));
+ }
+
+ if (root.contains(outputsKey)) {
+ QJsonArray outputs = root[outputsKey].toArray();
+ for (int i = 0; i < outputs.count(); ++i)
+ outVars.append(inOutVar(outputs[i].toObject()));
+ }
+
+ if (root.contains(uniformBlocksKey)) {
+ QJsonArray ubs = root[uniformBlocksKey].toArray();
+ for (int i = 0; i < ubs.count(); ++i) {
+ QJsonObject ubObj = ubs[i].toObject();
+ QShaderDescription::UniformBlock ub;
+ ub.blockName = ubObj[blockNameKey].toString();
+ ub.structName = ubObj[structNameKey].toString();
+ ub.size = ubObj[sizeKey].toInt();
+ if (ubObj.contains(bindingKey))
+ ub.binding = ubObj[bindingKey].toInt();
+ if (ubObj.contains(setKey))
+ ub.descriptorSet = ubObj[setKey].toInt();
+ QJsonArray members = ubObj[membersKey].toArray();
+ for (const QJsonValue &member : members)
+ ub.members.append(blockVar(member.toObject()));
+ uniformBlocks.append(ub);
+ }
+ }
+
+ if (root.contains(pushConstantBlocksKey)) {
+ QJsonArray pcs = root[pushConstantBlocksKey].toArray();
+ for (int i = 0; i < pcs.count(); ++i) {
+ QJsonObject pcObj = pcs[i].toObject();
+ QShaderDescription::PushConstantBlock pc;
+ pc.name = pcObj[nameKey].toString();
+ pc.size = pcObj[sizeKey].toInt();
+ QJsonArray members = pcObj[membersKey].toArray();
+ for (const QJsonValue &member : members)
+ pc.members.append(blockVar(member.toObject()));
+ pushConstantBlocks.append(pc);
+ }
+ }
+
+ if (root.contains(storageBlocksKey)) {
+ QJsonArray ubs = root[storageBlocksKey].toArray();
+ for (int i = 0; i < ubs.count(); ++i) {
+ QJsonObject sbObj = ubs[i].toObject();
+ QShaderDescription::StorageBlock sb;
+ sb.blockName = sbObj[blockNameKey].toString();
+ sb.instanceName = sbObj[instanceNameKey].toString();
+ sb.knownSize = sbObj[knownSizeKey].toInt();
+ if (sbObj.contains(bindingKey))
+ sb.binding = sbObj[bindingKey].toInt();
+ if (sbObj.contains(setKey))
+ sb.descriptorSet = sbObj[setKey].toInt();
+ QJsonArray members = sbObj[membersKey].toArray();
+ for (const QJsonValue &member : members)
+ sb.members.append(blockVar(member.toObject()));
+ storageBlocks.append(sb);
+ }
+ }
+
+ if (root.contains(combinedImageSamplersKey)) {
+ QJsonArray samplers = root[combinedImageSamplersKey].toArray();
+ for (int i = 0; i < samplers.count(); ++i)
+ combinedImageSamplers.append(inOutVar(samplers[i].toObject()));
+ }
+
+ if (root.contains(storageImagesKey)) {
+ QJsonArray images = root[storageImagesKey].toArray();
+ for (int i = 0; i < images.count(); ++i)
+ storageImages.append(inOutVar(images[i].toObject()));
+ }
+
+ if (root.contains(localSizeKey)) {
+ QJsonArray localSizeArr = root[localSizeKey].toArray();
+ if (localSizeArr.count() == 3) {
+ for (int i = 0; i < 3; ++i)
+ localSize[i] = localSizeArr[i].toInt();
+ }
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/rhi/qshaderdescription_p.h b/src/gui/rhi/qshaderdescription_p.h
new file mode 100644
index 0000000000..5a63b998cd
--- /dev/null
+++ b/src/gui/rhi/qshaderdescription_p.h
@@ -0,0 +1,281 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSHADERDESCRIPTION_H
+#define QSHADERDESCRIPTION_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/qtguiglobal.h>
+#include <QtCore/QString>
+#include <QtCore/QVector>
+#include <array>
+
+QT_BEGIN_NAMESPACE
+
+struct QShaderDescriptionPrivate;
+
+class Q_GUI_EXPORT QShaderDescription
+{
+public:
+ QShaderDescription();
+ QShaderDescription(const QShaderDescription &other);
+ QShaderDescription &operator=(const QShaderDescription &other);
+ ~QShaderDescription();
+ void detach();
+
+ bool isValid() const;
+
+ QByteArray toBinaryJson() const;
+ QByteArray toJson() const;
+
+ static QShaderDescription fromBinaryJson(const QByteArray &data);
+
+ enum VariableType {
+ Unknown = 0,
+
+ // do not reorder
+ Float,
+ Vec2,
+ Vec3,
+ Vec4,
+ Mat2,
+ Mat2x3,
+ Mat2x4,
+ Mat3,
+ Mat3x2,
+ Mat3x4,
+ Mat4,
+ Mat4x2,
+ Mat4x3,
+
+ Int,
+ Int2,
+ Int3,
+ Int4,
+
+ Uint,
+ Uint2,
+ Uint3,
+ Uint4,
+
+ Bool,
+ Bool2,
+ Bool3,
+ Bool4,
+
+ Double,
+ Double2,
+ Double3,
+ Double4,
+ DMat2,
+ DMat2x3,
+ DMat2x4,
+ DMat3,
+ DMat3x2,
+ DMat3x4,
+ DMat4,
+ DMat4x2,
+ DMat4x3,
+
+ Sampler1D,
+ Sampler2D,
+ Sampler2DMS,
+ Sampler3D,
+ SamplerCube,
+ Sampler1DArray,
+ Sampler2DArray,
+ Sampler2DMSArray,
+ Sampler3DArray,
+ SamplerCubeArray,
+ SamplerRect,
+ SamplerBuffer,
+
+ Image1D,
+ Image2D,
+ Image2DMS,
+ Image3D,
+ ImageCube,
+ Image1DArray,
+ Image2DArray,
+ Image2DMSArray,
+ Image3DArray,
+ ImageCubeArray,
+ ImageRect,
+ ImageBuffer,
+
+ Struct
+ };
+
+ enum ImageFormat {
+ // must match SPIR-V's ImageFormat
+ ImageFormatUnknown = 0,
+ ImageFormatRgba32f = 1,
+ ImageFormatRgba16f = 2,
+ ImageFormatR32f = 3,
+ ImageFormatRgba8 = 4,
+ ImageFormatRgba8Snorm = 5,
+ ImageFormatRg32f = 6,
+ ImageFormatRg16f = 7,
+ ImageFormatR11fG11fB10f = 8,
+ ImageFormatR16f = 9,
+ ImageFormatRgba16 = 10,
+ ImageFormatRgb10A2 = 11,
+ ImageFormatRg16 = 12,
+ ImageFormatRg8 = 13,
+ ImageFormatR16 = 14,
+ ImageFormatR8 = 15,
+ ImageFormatRgba16Snorm = 16,
+ ImageFormatRg16Snorm = 17,
+ ImageFormatRg8Snorm = 18,
+ ImageFormatR16Snorm = 19,
+ ImageFormatR8Snorm = 20,
+ ImageFormatRgba32i = 21,
+ ImageFormatRgba16i = 22,
+ ImageFormatRgba8i = 23,
+ ImageFormatR32i = 24,
+ ImageFormatRg32i = 25,
+ ImageFormatRg16i = 26,
+ ImageFormatRg8i = 27,
+ ImageFormatR16i = 28,
+ ImageFormatR8i = 29,
+ ImageFormatRgba32ui = 30,
+ ImageFormatRgba16ui = 31,
+ ImageFormatRgba8ui = 32,
+ ImageFormatR32ui = 33,
+ ImageFormatRgb10a2ui = 34,
+ ImageFormatRg32ui = 35,
+ ImageFormatRg16ui = 36,
+ ImageFormatRg8ui = 37,
+ ImageFormatR16ui = 38,
+ ImageFormatR8ui = 39
+ };
+
+ enum ImageFlag {
+ ReadOnlyImage = 1 << 0,
+ WriteOnlyImage = 1 << 1
+ };
+ Q_DECLARE_FLAGS(ImageFlags, ImageFlag)
+
+ // Optional data (like decorations) usually default to an otherwise invalid value (-1 or 0). This is intentional.
+
+ struct InOutVariable {
+ QString name;
+ VariableType type = Unknown;
+ int location = -1;
+ int binding = -1;
+ int descriptorSet = -1;
+ ImageFormat imageFormat = ImageFormatUnknown;
+ ImageFlags imageFlags;
+ };
+
+ struct BlockVariable {
+ QString name;
+ VariableType type = Unknown;
+ int offset = 0;
+ int size = 0;
+ QVector<int> arrayDims;
+ int arrayStride = 0;
+ int matrixStride = 0;
+ bool matrixIsRowMajor = false;
+ QVector<BlockVariable> structMembers;
+ };
+
+ struct UniformBlock {
+ QString blockName;
+ QString structName; // instanceName
+ int size = 0;
+ int binding = -1;
+ int descriptorSet = -1;
+ QVector<BlockVariable> members;
+ };
+
+ struct PushConstantBlock {
+ QString name;
+ int size = 0;
+ QVector<BlockVariable> members;
+ };
+
+ struct StorageBlock {
+ QString blockName;
+ QString instanceName;
+ int knownSize = 0;
+ int binding = -1;
+ int descriptorSet = -1;
+ QVector<BlockVariable> members;
+ };
+
+ QVector<InOutVariable> inputVariables() const;
+ QVector<InOutVariable> outputVariables() const;
+ QVector<UniformBlock> uniformBlocks() const;
+ QVector<PushConstantBlock> pushConstantBlocks() const;
+ QVector<StorageBlock> storageBlocks() const;
+ QVector<InOutVariable> combinedImageSamplers() const;
+ QVector<InOutVariable> storageImages() const;
+
+ std::array<uint, 3> computeShaderLocalSize() const;
+
+private:
+ QShaderDescriptionPrivate *d;
+ friend struct QShaderDescriptionPrivate;
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription &);
+#endif
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QShaderDescription::ImageFlags)
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription &);
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::InOutVariable &);
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::BlockVariable &);
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::UniformBlock &);
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::PushConstantBlock &);
+Q_GUI_EXPORT QDebug operator<<(QDebug, const QShaderDescription::StorageBlock &);
+#endif
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/qshaderdescription_p_p.h b/src/gui/rhi/qshaderdescription_p_p.h
new file mode 100644
index 0000000000..1caee24984
--- /dev/null
+++ b/src/gui/rhi/qshaderdescription_p_p.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gui module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSHADERDESCRIPTION_P_H
+#define QSHADERDESCRIPTION_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qshaderdescription_p.h"
+#include <QtCore/QVector>
+#include <QtCore/QAtomicInt>
+#include <QtCore/QJsonDocument>
+
+QT_BEGIN_NAMESPACE
+
+struct Q_GUI_EXPORT QShaderDescriptionPrivate
+{
+ QShaderDescriptionPrivate()
+ : ref(1)
+ {
+ localSize[0] = localSize[1] = localSize[2] = 0;
+ }
+
+ QShaderDescriptionPrivate(const QShaderDescriptionPrivate *other)
+ : ref(1),
+ inVars(other->inVars),
+ outVars(other->outVars),
+ uniformBlocks(other->uniformBlocks),
+ pushConstantBlocks(other->pushConstantBlocks),
+ storageBlocks(other->storageBlocks),
+ combinedImageSamplers(other->combinedImageSamplers),
+ storageImages(other->storageImages),
+ localSize(other->localSize)
+ {
+ }
+
+ static QShaderDescriptionPrivate *get(QShaderDescription *desc) { return desc->d; }
+ static const QShaderDescriptionPrivate *get(const QShaderDescription *desc) { return desc->d; }
+
+ QJsonDocument makeDoc();
+ void loadDoc(const QJsonDocument &doc);
+
+ QAtomicInt ref;
+ QVector<QShaderDescription::InOutVariable> inVars;
+ QVector<QShaderDescription::InOutVariable> outVars;
+ QVector<QShaderDescription::UniformBlock> uniformBlocks;
+ QVector<QShaderDescription::PushConstantBlock> pushConstantBlocks;
+ QVector<QShaderDescription::StorageBlock> storageBlocks;
+ QVector<QShaderDescription::InOutVariable> combinedImageSamplers;
+ QVector<QShaderDescription::InOutVariable> storageImages;
+ std::array<uint, 3> localSize;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/rhi/rhi.pri b/src/gui/rhi/rhi.pri
new file mode 100644
index 0000000000..d8607f1024
--- /dev/null
+++ b/src/gui/rhi/rhi.pri
@@ -0,0 +1,57 @@
+HEADERS += \
+ rhi/qrhi_p.h \
+ rhi/qrhi_p_p.h \
+ rhi/qrhiprofiler_p.h \
+ rhi/qrhiprofiler_p_p.h \
+ rhi/qrhinull_p.h \
+ rhi/qrhinull_p_p.h \
+ rhi/qshader_p.h \
+ rhi/qshader_p_p.h \
+ rhi/qshaderdescription_p.h \
+ rhi/qshaderdescription_p_p.h
+
+SOURCES += \
+ rhi/qrhi.cpp \
+ rhi/qrhiprofiler.cpp \
+ rhi/qrhinull.cpp \
+ rhi/qshaderdescription.cpp \
+ rhi/qshader.cpp
+
+qtConfig(opengl) {
+ HEADERS += \
+ rhi/qrhigles2_p.h \
+ rhi/qrhigles2_p_p.h
+ SOURCES += \
+ rhi/qrhigles2.cpp
+}
+
+qtConfig(vulkan) {
+ HEADERS += \
+ rhi/qrhivulkan_p.h \
+ rhi/qrhivulkan_p_p.h
+ SOURCES += \
+ rhi/qrhivulkan.cpp
+}
+
+win32 {
+ HEADERS += \
+ rhi/qrhid3d11_p.h \
+ rhi/qrhid3d11_p_p.h
+ SOURCES += \
+ rhi/qrhid3d11.cpp
+
+ LIBS += -ld3d11 -ldxgi -ldxguid -ld3dcompiler
+}
+
+# darwin {
+macos {
+ HEADERS += \
+ rhi/qrhimetal_p.h \
+ rhi/qrhimetal_p_p.h
+ SOURCES += \
+ rhi/qrhimetal.mm
+
+ LIBS += -framework AppKit -framework Metal
+}
+
+include($$PWD/../../3rdparty/VulkanMemoryAllocator.pri)
diff --git a/src/gui/text/qcssparser.cpp b/src/gui/text/qcssparser.cpp
index dc7e128bcd..b5489c7ed9 100644
--- a/src/gui/text/qcssparser.cpp
+++ b/src/gui/text/qcssparser.cpp
@@ -67,6 +67,7 @@ struct QCssKnownValue
static const QCssKnownValue properties[NumProperties - 1] = {
{ "-qt-background-role", QtBackgroundRole },
{ "-qt-block-indent", QtBlockIndent },
+ { "-qt-fg-texture-cachekey", QtForegroundTextureCacheKey },
{ "-qt-line-height-type", QtLineHeightType },
{ "-qt-list-indent", QtListIndent },
{ "-qt-list-number-prefix", QtListNumberPrefix },
diff --git a/src/gui/text/qcssparser_p.h b/src/gui/text/qcssparser_p.h
index 62578f75e5..b0fa4be682 100644
--- a/src/gui/text/qcssparser_p.h
+++ b/src/gui/text/qcssparser_p.h
@@ -196,6 +196,7 @@ enum Property {
LineHeight,
QtLineHeightType,
FontKerning,
+ QtForegroundTextureCacheKey,
NumProperties
};
diff --git a/src/gui/text/qdistancefield.cpp b/src/gui/text/qdistancefield.cpp
index 5967c8d3de..17824a5ba1 100644
--- a/src/gui/text/qdistancefield.cpp
+++ b/src/gui/text/qdistancefield.cpp
@@ -782,7 +782,7 @@ bool qt_fontHasNarrowOutlines(QFontEngine *fontEngine)
if (glyph != 0)
im = fe->alphaMapForGlyph(glyph, QFixed(), QTransform());
- Q_ASSERT(fe->ref.load() == 0);
+ Q_ASSERT(fe->ref.loadRelaxed() == 0);
delete fe;
return imageHasNarrowOutlines(im);
@@ -899,11 +899,6 @@ QDistanceField::QDistanceField(int width, int height)
{
}
-QDistanceField::QDistanceField(const QDistanceField &other)
-{
- d = other.d;
-}
-
QDistanceField &QDistanceField::operator=(const QDistanceField &)
= default;
diff --git a/src/gui/text/qdistancefield_p.h b/src/gui/text/qdistancefield_p.h
index dc5d5a9a02..d6d8edd85d 100644
--- a/src/gui/text/qdistancefield_p.h
+++ b/src/gui/text/qdistancefield_p.h
@@ -94,7 +94,6 @@ public:
QDistanceField(const QRawFont &font, glyph_t glyph, bool doubleResolution = false);
QDistanceField(QFontEngine *fontEngine, glyph_t glyph, bool doubleResolution = false);
QDistanceField(const QPainterPath &path, glyph_t glyph, bool doubleResolution = false);
- QDistanceField(const QDistanceField &other);
QDistanceField &operator=(const QDistanceField &other);
bool isNull() const;
diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp
index 164a2f60ab..5555422b82 100644
--- a/src/gui/text/qfont.cpp
+++ b/src/gui/text/qfont.cpp
@@ -336,7 +336,7 @@ QFontEngineData::QFontEngineData()
QFontEngineData::~QFontEngineData()
{
- Q_ASSERT(ref.load() == 0);
+ Q_ASSERT(ref.loadRelaxed() == 0);
for (int i = 0; i < QChar::ScriptCount; ++i) {
if (engines[i]) {
if (!engines[i]->ref.deref())
@@ -604,7 +604,7 @@ QFont::QFont(QFontPrivate *data)
*/
void QFont::detach()
{
- if (d->ref.load() == 1) {
+ if (d->ref.loadRelaxed() == 1) {
if (d->engineData && !d->engineData->ref.deref())
delete d->engineData;
d->engineData = 0;
@@ -625,7 +625,7 @@ void QFont::detach()
*/
void QFontPrivate::detachButKeepEngineData(QFont *font)
{
- if (font->d->ref.load() == 1)
+ if (font->d->ref.loadRelaxed() == 1)
return;
QFontEngineData *engineData = font->d->engineData;
@@ -2833,7 +2833,7 @@ void QFontCache::clear()
delete data;
} else {
FC_DEBUG("QFontCache::clear: engineData %p still has refcount %d",
- data, data->ref.load());
+ data, data->ref.loadRelaxed());
}
++it;
}
@@ -2857,7 +2857,7 @@ void QFontCache::clear()
delete engine;
} else if (cacheCount == 0) {
FC_DEBUG("QFontCache::clear: engine %p still has refcount %d",
- engine, engine->ref.load());
+ engine, engine->ref.loadRelaxed());
}
it.value().data = 0;
}
@@ -2927,7 +2927,7 @@ void QFontCache::updateHitCountAndTimeStamp(Engine &value)
FC_DEBUG("QFontCache: found font engine\n"
" %p: timestamp %4u hits %3u ref %2d/%2d, type %d",
value.data, value.timestamp, value.hits,
- value.data->ref.load(), engineCacheCount.value(value.data),
+ value.data->ref.loadRelaxed(), engineCacheCount.value(value.data),
value.data->type());
}
@@ -2937,7 +2937,7 @@ void QFontCache::insertEngine(const Key &key, QFontEngine *engine, bool insertMu
Q_ASSERT(key.multi == (engine->type() == QFontEngine::Multi));
#ifdef QFONTCACHE_DEBUG
- FC_DEBUG("QFontCache: inserting new engine %p, refcount %d", engine, engine->ref.load());
+ FC_DEBUG("QFontCache: inserting new engine %p, refcount %d", engine, engine->ref.loadRelaxed());
if (!insertMulti && engineCache.contains(key)) {
FC_DEBUG(" QFontCache already contains engine %p for key=(%g %g %d %d %d)",
engineCache.value(key).data, key.def.pointSize,
@@ -3026,9 +3026,9 @@ void QFontCache::decreaseCache()
EngineDataCache::ConstIterator it = engineDataCache.constBegin(),
end = engineDataCache.constEnd();
for (; it != end; ++it) {
- FC_DEBUG(" %p: ref %2d", it.value(), int(it.value()->ref.load()));
+ FC_DEBUG(" %p: ref %2d", it.value(), int(it.value()->ref.loadRelaxed()));
- if (it.value()->ref.load() != 1)
+ if (it.value()->ref.loadRelaxed() != 1)
in_use_cost += engine_data_cost;
}
}
@@ -3041,10 +3041,10 @@ void QFontCache::decreaseCache()
for (; it != end; ++it) {
FC_DEBUG(" %p: timestamp %4u hits %2u ref %2d/%2d, cost %u bytes",
it.value().data, it.value().timestamp, it.value().hits,
- it.value().data->ref.load(), engineCacheCount.value(it.value().data),
+ it.value().data->ref.loadRelaxed(), engineCacheCount.value(it.value().data),
it.value().data->cache_cost);
- if (it.value().data->ref.load() != 0)
+ if (it.value().data->ref.loadRelaxed() > engineCacheCount.value(it.value().data))
in_use_cost += it.value().data->cache_cost / engineCacheCount.value(it.value().data);
}
@@ -3093,7 +3093,7 @@ void QFontCache::decreaseCache()
// clean out all unused engine data
EngineDataCache::Iterator it = engineDataCache.begin();
while (it != engineDataCache.end()) {
- if (it.value()->ref.load() == 1) {
+ if (it.value()->ref.loadRelaxed() == 1) {
FC_DEBUG(" %p", it.value());
decreaseCost(sizeof(QFontEngineData));
it.value()->ref.deref();
@@ -3121,7 +3121,7 @@ void QFontCache::decreaseCache()
EngineCache::Iterator jt = end;
for ( ; it != end; ++it) {
- if (it.value().data->ref.load() != engineCacheCount.value(it.value().data))
+ if (it.value().data->ref.loadRelaxed() != engineCacheCount.value(it.value().data))
continue;
if (it.value().timestamp < oldest && it.value().hits <= least_popular) {
@@ -3135,7 +3135,7 @@ void QFontCache::decreaseCache()
if (it != end) {
FC_DEBUG(" %p: timestamp %4u hits %2u ref %2d/%2d, type %d",
it.value().data, it.value().timestamp, it.value().hits,
- it.value().data->ref.load(), engineCacheCount.value(it.value().data),
+ it.value().data->ref.loadRelaxed(), engineCacheCount.value(it.value().data),
it.value().data->type());
QFontEngine *fontEngine = it.value().data;
@@ -3150,7 +3150,7 @@ void QFontCache::decreaseCache()
}
}
// and delete the last occurrence
- Q_ASSERT(fontEngine->ref.load() == 0);
+ Q_ASSERT(fontEngine->ref.loadRelaxed() == 0);
decreaseCost(fontEngine->cache_cost);
delete fontEngine;
engineCacheCount.remove(fontEngine);
diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp
index 3cbda0facd..5350a9c5ec 100644
--- a/src/gui/text/qfontdatabase.cpp
+++ b/src/gui/text/qfontdatabase.cpp
@@ -695,7 +695,8 @@ static QStringList familyList(const QFontDef &req)
if ((str.startsWith(QLatin1Char('"')) && str.endsWith(QLatin1Char('"')))
|| (str.startsWith(QLatin1Char('\'')) && str.endsWith(QLatin1Char('\''))))
str = str.mid(1, str.length() - 2);
- family_list << str.toString();
+ if (!family_list.contains(str))
+ family_list << str.toString();
}
}
// append the substitute list for each family in family_list
@@ -977,7 +978,7 @@ QFontEngine *loadSingleEngine(int script,
if (!engine->supportsScript(QChar::Script(script))) {
qWarning(" OpenType support missing for \"%s\", script %d",
+ qPrintable(def.family), script);
- if (engine->ref.load() == 0)
+ if (engine->ref.loadRelaxed() == 0)
delete engine;
return 0;
}
@@ -2826,7 +2827,7 @@ void QFontDatabase::load(const QFontPrivate *d, int script)
fe = QFontDatabase::findFont(req, script);
if (fe) {
if (fe->type() == QFontEngine::Box && !req.families.at(0).isEmpty()) {
- if (fe->ref.load() == 0)
+ if (fe->ref.loadRelaxed() == 0)
delete fe;
fe = 0;
} else {
diff --git a/src/gui/text/qglyphrun.cpp b/src/gui/text/qglyphrun.cpp
index bd44e11dce..3c16c3bf62 100644
--- a/src/gui/text/qglyphrun.cpp
+++ b/src/gui/text/qglyphrun.cpp
@@ -137,7 +137,7 @@ QGlyphRun::~QGlyphRun()
*/
void QGlyphRun::detach()
{
- if (d->ref.load() != 1)
+ if (d->ref.loadRelaxed() != 1)
d.detach();
}
diff --git a/src/gui/text/qplatformfontdatabase.cpp b/src/gui/text/qplatformfontdatabase.cpp
index 715b00d838..90322b24da 100644
--- a/src/gui/text/qplatformfontdatabase.cpp
+++ b/src/gui/text/qplatformfontdatabase.cpp
@@ -235,7 +235,7 @@ QSupportedWritingSystems::~QSupportedWritingSystems()
*/
void QSupportedWritingSystems::detach()
{
- if (d->ref.load() != 1) {
+ if (d->ref.loadRelaxed() != 1) {
QWritingSystemsPrivate *newd = new QWritingSystemsPrivate(d);
if (!d->ref.deref())
delete d;
diff --git a/src/gui/text/qrawfont_p.h b/src/gui/text/qrawfont_p.h
index dced165475..03259a94ed 100644
--- a/src/gui/text/qrawfont_p.h
+++ b/src/gui/text/qrawfont_p.h
@@ -87,7 +87,7 @@ public:
~QRawFontPrivate()
{
#ifndef QT_NO_DEBUG
- Q_ASSERT(ref.load() == 0);
+ Q_ASSERT(ref.loadRelaxed() == 0);
#endif
cleanUp();
}
diff --git a/src/gui/text/qstatictext.cpp b/src/gui/text/qstatictext.cpp
index dd894f4d32..490e0b6b8f 100644
--- a/src/gui/text/qstatictext.cpp
+++ b/src/gui/text/qstatictext.cpp
@@ -181,7 +181,7 @@ QStaticText::QStaticText(const QStaticText &other)
*/
QStaticText::~QStaticText()
{
- Q_ASSERT(!data || data->ref.load() >= 1);
+ Q_ASSERT(!data || data->ref.loadRelaxed() >= 1);
}
/*!
@@ -189,7 +189,7 @@ QStaticText::~QStaticText()
*/
void QStaticText::detach()
{
- if (data->ref.load() != 1)
+ if (data->ref.loadRelaxed() != 1)
data.detach();
}
diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp
index 899801ca39..2ec3f41f42 100644
--- a/src/gui/text/qtextdocument.cpp
+++ b/src/gui/text/qtextdocument.cpp
@@ -1364,6 +1364,8 @@ QTextCursor QTextDocument::find(const QString &subString, int from, FindFlags op
blockOffset = 0;
}
} else {
+ if (blockOffset == block.length() - 1)
+ --blockOffset; // make sure to skip end-of-paragraph character
while (block.isValid()) {
if (findInBlock(block, subString, blockOffset, options, &cursor))
return cursor;
@@ -2468,9 +2470,19 @@ bool QTextHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format)
if (format.foreground() != defaultCharFormat.foreground()
&& format.foreground().style() != Qt::NoBrush) {
- html += QLatin1String(" color:");
- html += colorValue(format.foreground().color());
- html += QLatin1Char(';');
+ QBrush brush = format.foreground();
+ if (brush.style() == Qt::TexturePattern) {
+ const bool isPixmap = qHasPixmapTexture(brush);
+ const qint64 cacheKey = isPixmap ? brush.texture().cacheKey() : brush.textureImage().cacheKey();
+
+ html += QLatin1String(" -qt-fg-texture-cachekey:");
+ html += QString::number(cacheKey);
+ html += QLatin1String(";");
+ } else {
+ html += QLatin1String(" color:");
+ html += colorValue(brush.color());
+ html += QLatin1Char(';');
+ }
attributesEmitted = true;
}
diff --git a/src/gui/text/qtextdocument.h b/src/gui/text/qtextdocument.h
index edb6bd9310..2fadc40cd2 100644
--- a/src/gui/text/qtextdocument.h
+++ b/src/gui/text/qtextdocument.h
@@ -225,6 +225,7 @@ public:
void print(QPagedPaintDevice *printer) const;
enum ResourceType {
+ UnknownResource = 0,
HtmlResource = 1,
ImageResource = 2,
StyleSheetResource = 3,
@@ -232,6 +233,7 @@ public:
UserResource = 100
};
+ Q_ENUM(ResourceType)
QVariant resource(int type, const QUrl &name) const;
void addResource(int type, const QUrl &name, const QVariant &resource);
diff --git a/src/gui/text/qtextdocument_p.h b/src/gui/text/qtextdocument_p.h
index a8e17bfc08..df00fb7d84 100644
--- a/src/gui/text/qtextdocument_p.h
+++ b/src/gui/text/qtextdocument_p.h
@@ -357,6 +357,7 @@ public:
void mergeCachedResources(const QTextDocumentPrivate *priv);
+ friend struct QTextHtmlParserNode;
friend class QTextHtmlExporter;
friend class QTextCursor;
};
diff --git a/src/gui/text/qtextdocumentlayout.cpp b/src/gui/text/qtextdocumentlayout.cpp
index e5cfa7f46e..a1b21b111b 100644
--- a/src/gui/text/qtextdocumentlayout.cpp
+++ b/src/gui/text/qtextdocumentlayout.cpp
@@ -973,8 +973,14 @@ void QTextDocumentLayoutPrivate::drawFrame(const QPointF &offset, QPainter *pain
if (pageHeight <= 0)
pageHeight = QFIXED_MAX;
- const int tableStartPage = (td->position.y / pageHeight).truncate();
- const int tableEndPage = ((td->position.y + td->size.height) / pageHeight).truncate();
+ QFixed absYPos = td->position.y;
+ QTextFrame *parentFrame = table->parentFrame();
+ while (parentFrame) {
+ absYPos += data(parentFrame)->position.y;
+ parentFrame = parentFrame->parentFrame();
+ }
+ const int tableStartPage = (absYPos / pageHeight).truncate();
+ const int tableEndPage = ((absYPos + td->size.height) / pageHeight).truncate();
qreal border = td->border.toReal();
drawFrameDecoration(painter, frame, fd, context.clip, frameRect);
@@ -1614,7 +1620,7 @@ QRectF QTextDocumentLayoutPrivate::layoutTable(QTextTable *table, int layoutFrom
for (int i = 0; i < children.count(); ++i) {
QTextFrame *frame = children.at(i);
QTextTableCell cell = table->cellAt(frame->firstPosition());
- td->childFrameMap.insertMulti(cell.row() + cell.column() * rows, frame);
+ td->childFrameMap.insert(cell.row() + cell.column() * rows, frame);
}
}
diff --git a/src/gui/text/qtexthtmlparser.cpp b/src/gui/text/qtexthtmlparser.cpp
index 642f0893b4..0b1a23f399 100644
--- a/src/gui/text/qtexthtmlparser.cpp
+++ b/src/gui/text/qtexthtmlparser.cpp
@@ -1335,6 +1335,17 @@ void QTextHtmlParserNode::applyCssDeclarations(const QVector<QCss::Declaration>
default: break;
}
break;
+
+ case QCss::QtForegroundTextureCacheKey:
+ {
+ if (resourceProvider != nullptr && resourceProvider->docHandle() != nullptr) {
+ bool ok;
+ qint64 searchKey = decl.d->values.first().variant.toLongLong(&ok);
+ if (ok)
+ applyForegroundImage(searchKey, resourceProvider);
+ }
+ break;
+ }
default: break;
}
}
@@ -1367,6 +1378,37 @@ void QTextHtmlParserNode::applyCssDeclarations(const QVector<QCss::Declaration>
#endif // QT_NO_CSSPARSER
+void QTextHtmlParserNode::applyForegroundImage(qint64 searchKey, const QTextDocument *resourceProvider)
+{
+ QTextDocumentPrivate *priv = resourceProvider->docHandle();
+ for (int i = 0; i < priv->formats.numFormats(); ++i) {
+ QTextCharFormat format = priv->formats.charFormat(i);
+ if (format.isValid()) {
+ QBrush brush = format.foreground();
+ if (brush.style() == Qt::TexturePattern) {
+ const bool isPixmap = qHasPixmapTexture(brush);
+
+ if (isPixmap && QCoreApplication::instance()->thread() != QThread::currentThread()) {
+ qWarning("Can't apply QPixmap outside of GUI thread");
+ return;
+ }
+
+ const qint64 cacheKey = isPixmap ? brush.texture().cacheKey() : brush.textureImage().cacheKey();
+ if (cacheKey == searchKey) {
+ QBrush b;
+ if (isPixmap)
+ b.setTexture(brush.texture());
+ else
+ b.setTextureImage(brush.textureImage());
+ b.setStyle(Qt::TexturePattern);
+ charFormat.setForeground(b);
+ }
+ }
+ }
+ }
+
+}
+
void QTextHtmlParserNode::applyBackgroundImage(const QString &url, const QTextDocument *resourceProvider)
{
if (!url.isEmpty() && resourceProvider) {
diff --git a/src/gui/text/qtexthtmlparser_p.h b/src/gui/text/qtexthtmlparser_p.h
index 6ce294d211..c174b54a61 100644
--- a/src/gui/text/qtexthtmlparser_p.h
+++ b/src/gui/text/qtexthtmlparser_p.h
@@ -251,6 +251,7 @@ struct QTextHtmlParserNode {
void setListStyle(const QVector<QCss::Value> &cssValues);
#endif
+ void applyForegroundImage(qint64 cacheKey, const QTextDocument *resourceProvider);
void applyBackgroundImage(const QString &url, const QTextDocument *resourceProvider);
bool hasOnlyWhitespace() const;
diff --git a/src/gui/text/qtextmarkdownimporter.cpp b/src/gui/text/qtextmarkdownimporter.cpp
index 8a9bb3953c..b96263f5fc 100644
--- a/src/gui/text/qtextmarkdownimporter.cpp
+++ b/src/gui/text/qtextmarkdownimporter.cpp
@@ -440,7 +440,7 @@ int QTextMarkdownImporter::cbText(int textType, const char *text, unsigned size)
#endif
case MD_TEXT_HTML:
// count how many tags are opened and how many are closed
-#if QT_CONFIG(regularexpression)
+#if QT_CONFIG(regularexpression) && QT_CONFIG(texthtmlparser)
{
int startIdx = 0;
while ((startIdx = s.indexOf(openingBracket, startIdx)) >= 0) {
diff --git a/src/network/access/qnetworkaccessbackend.cpp b/src/network/access/qnetworkaccessbackend.cpp
index 272dd22097..848fc84de7 100644
--- a/src/network/access/qnetworkaccessbackend.cpp
+++ b/src/network/access/qnetworkaccessbackend.cpp
@@ -83,7 +83,7 @@ QNetworkAccessBackendFactory::QNetworkAccessBackendFactory()
QNetworkAccessBackendFactory::~QNetworkAccessBackendFactory()
{
- if (QNetworkAccessBackendFactoryData::valid.load()) {
+ if (QNetworkAccessBackendFactoryData::valid.loadRelaxed()) {
QMutexLocker locker(&factoryData()->mutex);
factoryData()->removeAll(this);
}
@@ -92,7 +92,7 @@ QNetworkAccessBackendFactory::~QNetworkAccessBackendFactory()
QNetworkAccessBackend *QNetworkAccessManagerPrivate::findBackend(QNetworkAccessManager::Operation op,
const QNetworkRequest &request)
{
- if (QNetworkAccessBackendFactoryData::valid.load()) {
+ if (QNetworkAccessBackendFactoryData::valid.loadRelaxed()) {
QMutexLocker locker(&factoryData()->mutex);
QNetworkAccessBackendFactoryData::ConstIterator it = factoryData()->constBegin(),
end = factoryData()->constEnd();
@@ -110,7 +110,7 @@ QNetworkAccessBackend *QNetworkAccessManagerPrivate::findBackend(QNetworkAccessM
QStringList QNetworkAccessManagerPrivate::backendSupportedSchemes() const
{
- if (QNetworkAccessBackendFactoryData::valid.load()) {
+ if (QNetworkAccessBackendFactoryData::valid.loadRelaxed()) {
QMutexLocker locker(&factoryData()->mutex);
QNetworkAccessBackendFactoryData::ConstIterator it = factoryData()->constBegin();
QNetworkAccessBackendFactoryData::ConstIterator end = factoryData()->constEnd();
diff --git a/src/network/access/qnetworkaccesscache.cpp b/src/network/access/qnetworkaccesscache.cpp
index 00bb18cb82..b694a2c999 100644
--- a/src/network/access/qnetworkaccesscache.cpp
+++ b/src/network/access/qnetworkaccesscache.cpp
@@ -40,11 +40,12 @@
#include "qnetworkaccesscache_p.h"
#include "QtCore/qpointer.h"
#include "QtCore/qdatetime.h"
-#include "QtCore/qqueue.h"
#include "qnetworkaccessmanager_p.h"
#include "qnetworkreply_p.h"
#include "qnetworkrequest.h"
+#include <vector>
+
QT_BEGIN_NAMESPACE
enum ExpiryTimeEnum {
@@ -63,7 +64,7 @@ namespace {
struct QNetworkAccessCache::Node
{
QDateTime timestamp;
- QQueue<Receiver> receiverQueue;
+ std::vector<Receiver> receiverQueue;
QByteArray key;
Node *older, *newer;
@@ -277,10 +278,7 @@ bool QNetworkAccessCache::requestEntry(const QByteArray &key, QObject *target, c
// object is not shareable and is in use
// queue for later use
Q_ASSERT(node->older == 0 && node->newer == 0);
- Receiver receiver;
- receiver.object = target;
- receiver.member = member;
- node->receiverQueue.enqueue(receiver);
+ node->receiverQueue.push_back({target, member});
// request queued
return true;
@@ -331,17 +329,19 @@ void QNetworkAccessCache::releaseEntry(const QByteArray &key)
Q_ASSERT(node->useCount > 0);
// are there other objects waiting?
- if (!node->receiverQueue.isEmpty()) {
+ const auto objectStillExists = [](const Receiver &r) { return !r.object.isNull(); };
+
+ auto &queue = node->receiverQueue;
+ auto qit = std::find_if(queue.begin(), queue.end(), objectStillExists);
+
+ const Receiver receiver = qit == queue.end() ? Receiver{} : std::move(*qit++) ;
+
+ queue.erase(queue.begin(), qit);
+
+ if (receiver.object) {
// queue another activation
- Receiver receiver;
- do {
- receiver = node->receiverQueue.dequeue();
- } while (receiver.object.isNull() && !node->receiverQueue.isEmpty());
-
- if (!receiver.object.isNull()) {
- emitEntryReady(node, receiver.object, receiver.member);
- return;
- }
+ emitEntryReady(node, receiver.object, receiver.member);
+ return;
}
if (!--node->useCount) {
diff --git a/src/network/access/qnetworkreplywasmimpl.cpp b/src/network/access/qnetworkreplywasmimpl.cpp
index 9f8a42ad89..9f6422a107 100644
--- a/src/network/access/qnetworkreplywasmimpl.cpp
+++ b/src/network/access/qnetworkreplywasmimpl.cpp
@@ -59,6 +59,9 @@ using namespace emscripten;
static void q_requestErrorCallback(val event)
{
+ if (event.isNull() || event.isUndefined())
+ return;
+
val xhr = event["target"];
quintptr func = xhr["data-handler"].as<quintptr>();
@@ -77,19 +80,24 @@ static void q_requestErrorCallback(val event)
static void q_progressCallback(val event)
{
+ if (event.isNull() || event.isUndefined())
+ return;
+
val xhr = event["target"];
QNetworkReplyWasmImplPrivate *reply =
reinterpret_cast<QNetworkReplyWasmImplPrivate*>(xhr["data-handler"].as<quintptr>());
Q_ASSERT(reply);
- if (xhr["lengthComputable"].as<bool>() && xhr["status"].as<int>() < 400)
- reply->emitDataReadProgress(xhr["loaded"].as<qint64>(), xhr["total"].as<qint64>());
-
+ if (xhr["status"].as<int>() < 400)
+ reply->emitDataReadProgress(event["loaded"].as<int>(), event["total"].as<int>());
}
static void q_loadCallback(val event)
{
+ if (event.isNull() || event.isUndefined())
+ return;
+
val xhr = event["target"];
QNetworkReplyWasmImplPrivate *reply =
@@ -121,6 +129,7 @@ static void q_loadCallback(val event)
reader.set("data-handler", xhr["data-handler"]);
reader.call<void>("readAsArrayBuffer", blob);
+ val::global("Module").delete_(reader);
}
@@ -136,6 +145,9 @@ static void q_loadCallback(val event)
static void q_responseHeadersCallback(val event)
{
+ if (event.isNull() || event.isUndefined())
+ return;
+
val xhr = event["target"];
if (xhr["readyState"].as<int>() == 2) { // HEADERS_RECEIVED
@@ -152,12 +164,18 @@ static void q_responseHeadersCallback(val event)
static void q_readBinary(val event)
{
+ if (event.isNull() || event.isUndefined())
+ return;
+
val fileReader = event["target"];
QNetworkReplyWasmImplPrivate *reply =
reinterpret_cast<QNetworkReplyWasmImplPrivate*>(fileReader["data-handler"].as<quintptr>());
Q_ASSERT(reply);
+ if (reply->state == QNetworkReplyPrivate::Finished || reply->state == QNetworkReplyPrivate::Aborted)
+ return;
+
// Set up source typed array
val result = fileReader["result"]; // ArrayBuffer
val Uint8Array = val::global("Uint8Array");
@@ -171,6 +189,10 @@ static void q_readBinary(val event)
reinterpret_cast<quintptr>(buffer.data()), size);
destinationTypedArray.call<void>("set", sourceTypedArray);
reply->dataReceived(buffer, buffer.size());
+
+ event.delete_(fileReader);
+ Uint8Array.delete_(sourceTypedArray);
+
QCoreApplication::processEvents();
}
@@ -194,14 +216,21 @@ QNetworkReplyWasmImplPrivate::QNetworkReplyWasmImplPrivate()
QNetworkReplyWasmImplPrivate::~QNetworkReplyWasmImplPrivate()
{
+ m_xhr.set("onerror", val::null());
+ m_xhr.set("onload", val::null());
+ m_xhr.set("onprogress", val::null());
+ m_xhr.set("onreadystatechange", val::null());
+ m_xhr.set("data-handler", val::null());
}
-QNetworkReplyWasmImpl::~QNetworkReplyWasmImpl()
+QNetworkReplyWasmImpl::QNetworkReplyWasmImpl(QObject *parent)
+ : QNetworkReply(*new QNetworkReplyWasmImplPrivate(), parent)
{
+ Q_D( QNetworkReplyWasmImpl);
+ d->state = QNetworkReplyPrivate::Idle;
}
-QNetworkReplyWasmImpl::QNetworkReplyWasmImpl(QObject *parent)
- : QNetworkReply(*new QNetworkReplyWasmImplPrivate(), parent)
+QNetworkReplyWasmImpl::~QNetworkReplyWasmImpl()
{
}
@@ -226,19 +255,23 @@ QByteArray QNetworkReplyWasmImpl::methodName() const
void QNetworkReplyWasmImpl::close()
{
+ QNetworkReply::close();
setFinished(true);
emit finished();
-
- QNetworkReply::close();
}
void QNetworkReplyWasmImpl::abort()
{
- Q_D(const QNetworkReplyWasmImpl);
- setError( QNetworkReply::OperationCanceledError, QStringLiteral("Operation canceled"));
+ Q_D( QNetworkReplyWasmImpl);
+ if (d->state == QNetworkReplyPrivate::Finished || d->state == QNetworkReplyPrivate::Aborted)
+ return;
+
+ setError(QNetworkReply::OperationCanceledError, QStringLiteral("Operation canceled"));
+
d->doAbort();
close();
+ d->state = QNetworkReplyPrivate::Aborted;
}
qint64 QNetworkReplyWasmImpl::bytesAvailable() const
diff --git a/src/network/bearer/qbearerengine.cpp b/src/network/bearer/qbearerengine.cpp
index 677da08cb6..c06adb202f 100644
--- a/src/network/bearer/qbearerengine.cpp
+++ b/src/network/bearer/qbearerengine.cpp
@@ -56,7 +56,7 @@ static void cleanUpConfigurations(QHash<QString, QNetworkConfigurationPrivatePoi
static bool hasUsedConfiguration(const QHash<QString, QNetworkConfigurationPrivatePointer> &configurations)
{
auto isUsed = [](const QNetworkConfigurationPrivatePointer &ptr) {
- return ptr->ref.load() > 1;
+ return ptr->ref.loadRelaxed() > 1;
};
const auto end = configurations.end();
return std::find_if(configurations.begin(), end, isUsed) != end;
diff --git a/src/network/bearer/qnetworkconfigmanager.cpp b/src/network/bearer/qnetworkconfigmanager.cpp
index 81b5e01d6a..cd87c3669c 100644
--- a/src/network/bearer/qnetworkconfigmanager.cpp
+++ b/src/network/bearer/qnetworkconfigmanager.cpp
@@ -233,19 +233,20 @@ QNetworkConfigurationManager::QNetworkConfigurationManager(QObject *parent)
: QObject(parent)
{
QNetworkConfigurationManagerPrivate *priv = qNetworkConfigurationManagerPrivate();
-
- connect(priv, SIGNAL(configurationAdded(QNetworkConfiguration)),
- this, SIGNAL(configurationAdded(QNetworkConfiguration)));
- connect(priv, SIGNAL(configurationRemoved(QNetworkConfiguration)),
- this, SIGNAL(configurationRemoved(QNetworkConfiguration)));
- connect(priv, SIGNAL(configurationChanged(QNetworkConfiguration)),
- this, SIGNAL(configurationChanged(QNetworkConfiguration)));
- connect(priv, SIGNAL(onlineStateChanged(bool)),
- this, SIGNAL(onlineStateChanged(bool)));
- connect(priv, SIGNAL(configurationUpdateComplete()),
- this, SIGNAL(updateCompleted()));
-
- priv->enablePolling();
+ if (priv) {
+ connect(priv, SIGNAL(configurationAdded(QNetworkConfiguration)),
+ this, SIGNAL(configurationAdded(QNetworkConfiguration)));
+ connect(priv, SIGNAL(configurationRemoved(QNetworkConfiguration)),
+ this, SIGNAL(configurationRemoved(QNetworkConfiguration)));
+ connect(priv, SIGNAL(configurationChanged(QNetworkConfiguration)),
+ this, SIGNAL(configurationChanged(QNetworkConfiguration)));
+ connect(priv, SIGNAL(onlineStateChanged(bool)),
+ this, SIGNAL(onlineStateChanged(bool)));
+ connect(priv, SIGNAL(configurationUpdateComplete()),
+ this, SIGNAL(updateCompleted()));
+
+ priv->enablePolling();
+ }
}
/*!
diff --git a/src/network/bearer/qnetworkconfiguration.cpp b/src/network/bearer/qnetworkconfiguration.cpp
index f5ced0693a..19bc44e02a 100644
--- a/src/network/bearer/qnetworkconfiguration.cpp
+++ b/src/network/bearer/qnetworkconfiguration.cpp
@@ -414,34 +414,7 @@ bool QNetworkConfiguration::isRoamingAvailable() const
*/
QList<QNetworkConfiguration> QNetworkConfiguration::children() const
{
- QList<QNetworkConfiguration> results;
-
- if (!d)
- return results;
-
- QMutexLocker locker(&d->mutex);
-
- if (d->type != QNetworkConfiguration::ServiceNetwork || !d->isValid)
- return results;
-
- for (auto it = d->serviceNetworkMembers.begin(), end = d->serviceNetworkMembers.end(); it != end;) {
- QNetworkConfigurationPrivatePointer p = it.value();
- //if we have an invalid member get rid of it -> was deleted earlier on
- {
- QMutexLocker childLocker(&p->mutex);
-
- if (!p->isValid) {
- it = d->serviceNetworkMembers.erase(it);
- continue;
- }
- }
- QNetworkConfiguration item;
- item.d = p;
- results << item;
- ++it;
- }
-
- return results;
+ return {};
}
/*!
diff --git a/src/network/bearer/qnetworkconfiguration_p.h b/src/network/bearer/qnetworkconfiguration_p.h
index 1b1ece39b7..2213d2ae14 100644
--- a/src/network/bearer/qnetworkconfiguration_p.h
+++ b/src/network/bearer/qnetworkconfiguration_p.h
@@ -72,13 +72,6 @@ public:
isValid(false), roamingSupported(false),
timeout(DefaultTimeout)
{}
- virtual ~QNetworkConfigurationPrivate()
- {
- //release pointers to member configurations
- serviceNetworkMembers.clear();
- }
-
- QMap<unsigned int, QNetworkConfigurationPrivatePointer> serviceNetworkMembers;
mutable QMutex mutex;
diff --git a/src/network/bearer/qnetworksession.cpp b/src/network/bearer/qnetworksession.cpp
index 471d322998..1636bcee97 100644
--- a/src/network/bearer/qnetworksession.cpp
+++ b/src/network/bearer/qnetworksession.cpp
@@ -258,7 +258,8 @@ QNetworkSession::QNetworkSession(const QNetworkConfiguration &connectionConfig,
// invalid configuration
if (!connectionConfig.identifier().isEmpty()) {
- const auto engines = qNetworkConfigurationManagerPrivate()->engines();
+ auto priv = qNetworkConfigurationManagerPrivate();
+ const auto engines = priv ? priv->engines() : QList<QBearerEngine *>();
for (QBearerEngine *engine : engines) {
if (engine->hasIdentifier(connectionConfig.identifier())) {
d = engine->createSessionBackend();
diff --git a/src/network/doc/src/dontdocument.qdoc b/src/network/doc/src/dontdocument.qdoc
new file mode 100644
index 0000000000..fe2e54b34c
--- /dev/null
+++ b/src/network/doc/src/dontdocument.qdoc
@@ -0,0 +1,30 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \dontdocument (QTypeInfo QMetaTypeId QIPv6Address)
+*/
diff --git a/src/network/doc/src/ssl.qdoc b/src/network/doc/src/ssl.qdoc
index a3af1d0477..e485a1b393 100644
--- a/src/network/doc/src/ssl.qdoc
+++ b/src/network/doc/src/ssl.qdoc
@@ -77,11 +77,12 @@
\section1 Import and Export Restrictions
- Due to import and export restrictions in some parts of the world, we
- are unable to supply the OpenSSL Toolkit with Qt packages. Developers wishing
- to use SSL communication in their deployed applications should either ensure
- that their users have the appropriate libraries installed, or they should
- consult a suitably qualified legal professional to ensure that applications
- using code from the OpenSSL project are correctly certified for import
- and export in relevant regions of the world.
+ Qt binary installers include the OpenSSL libraries used by QtNetwork. However,
+ those are not automatically deployed with applications that are built with Qt.
+ Import and export restrictions apply for some types of software, and for
+ some parts of the world. Developers wishing to use SSL communication in their
+ deployed applications should either ensure that their users have the appropriate
+ libraries installed, or they should consult a suitably qualified legal
+ professional to ensure that applications using code from the OpenSSL project
+ are correctly certified for import and export in relevant regions of the world.
*/
diff --git a/src/network/kernel/qnetworkproxy.cpp b/src/network/kernel/qnetworkproxy.cpp
index 3646a9526a..2f840e9e13 100644
--- a/src/network/kernel/qnetworkproxy.cpp
+++ b/src/network/kernel/qnetworkproxy.cpp
@@ -483,7 +483,7 @@ public:
template<> void QSharedDataPointer<QNetworkProxyPrivate>::detach()
{
- if (d && d->ref.load() == 1)
+ if (d && d->ref.loadRelaxed() == 1)
return;
QNetworkProxyPrivate *x = (d ? new QNetworkProxyPrivate(*d)
: new QNetworkProxyPrivate);
@@ -925,7 +925,7 @@ public:
template<> void QSharedDataPointer<QNetworkProxyQueryPrivate>::detach()
{
- if (d && d->ref.load() == 1)
+ if (d && d->ref.loadRelaxed() == 1)
return;
QNetworkProxyQueryPrivate *x = (d ? new QNetworkProxyQueryPrivate(*d)
: new QNetworkProxyQueryPrivate);
diff --git a/src/network/ssl/qdtls.cpp b/src/network/ssl/qdtls.cpp
index 3185bfa124..a2280a7d10 100644
--- a/src/network/ssl/qdtls.cpp
+++ b/src/network/ssl/qdtls.cpp
@@ -342,7 +342,7 @@ QT_BEGIN_NAMESPACE
QSslConfiguration QDtlsBasePrivate::configuration() const
{
auto copyPrivate = new QSslConfigurationPrivate(dtlsConfiguration);
- copyPrivate->ref.store(0); // the QSslConfiguration constructor refs up
+ copyPrivate->ref.storeRelaxed(0); // the QSslConfiguration constructor refs up
QSslConfiguration copy(copyPrivate);
copyPrivate->sessionCipher = sessionCipher;
copyPrivate->sessionProtocol = sessionProtocol;
diff --git a/src/network/ssl/qdtls_openssl.cpp b/src/network/ssl/qdtls_openssl.cpp
index 8be53df24f..d9ddcceb40 100644
--- a/src/network/ssl/qdtls_openssl.cpp
+++ b/src/network/ssl/qdtls_openssl.cpp
@@ -729,7 +729,7 @@ bool DtlsState::initCtxAndConnection(QDtlsBasePrivate *dtlsBase)
// Create a deep copy of our configuration
auto configurationCopy = new QSslConfigurationPrivate(dtlsBase->dtlsConfiguration);
- configurationCopy->ref.store(0); // the QSslConfiguration constructor refs up
+ configurationCopy->ref.storeRelaxed(0); // the QSslConfiguration constructor refs up
// DTLSTODO: check we do not set something DTLS-incompatible there ...
TlsContext newContext(QSslContext::sharedFromConfiguration(dtlsBase->mode,
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index fe81bd5fcf..ca6c58117d 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -924,7 +924,7 @@ QSslConfiguration QSslSocket::sslConfiguration() const
// create a deep copy of our configuration
QSslConfigurationPrivate *copy = new QSslConfigurationPrivate(d->configuration);
- copy->ref.store(0); // the QSslConfiguration constructor refs up
+ copy->ref.storeRelaxed(0); // the QSslConfiguration constructor refs up
copy->sessionCipher = d->sessionCipher();
copy->sessionProtocol = d->sessionProtocol();
@@ -2378,7 +2378,7 @@ void QSslConfigurationPrivate::deepCopyDefaultConfiguration(QSslConfigurationPri
if (!global)
return;
- ptr->ref.store(1);
+ ptr->ref.storeRelaxed(1);
ptr->peerCertificate = global->peerCertificate;
ptr->peerCertificateChain = global->peerCertificateChain;
ptr->localCertificateChain = global->localCertificateChain;
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
index 6a8269b521..d4bad1b1a5 100644
--- a/src/network/ssl/qsslsocket_openssl.cpp
+++ b/src/network/ssl/qsslsocket_openssl.cpp
@@ -71,6 +71,10 @@
#include "qwindowscarootfetcher_p.h"
#endif
+#if !QT_CONFIG(opensslv11)
+#include <openssl/x509_vfy.h>
+#endif
+
#include <QtCore/qdatetime.h>
#include <QtCore/qdebug.h>
#include <QtCore/qdir.h>
@@ -137,6 +141,55 @@ static unsigned int q_ssl_psk_server_callback(SSL *ssl,
Q_ASSERT(d);
return d->tlsPskServerCallback(identity, psk, max_psk_len);
}
+
+#ifdef TLS1_3_VERSION
+#ifndef OPENSSL_NO_PSK
+static unsigned int q_ssl_psk_restore_client(SSL *ssl,
+ const char *hint,
+ char *identity, unsigned int max_identity_len,
+ unsigned char *psk, unsigned int max_psk_len)
+{
+ Q_UNUSED(hint);
+ Q_UNUSED(identity);
+ Q_UNUSED(max_identity_len);
+ Q_UNUSED(psk);
+ Q_UNUSED(max_psk_len);
+
+#ifdef QT_DEBUG
+ QSslSocketBackendPrivate *d = reinterpret_cast<QSslSocketBackendPrivate *>(q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData));
+ Q_ASSERT(d);
+ Q_ASSERT(d->mode == QSslSocket::SslClientMode);
+#endif
+ q_SSL_set_psk_client_callback(ssl, &q_ssl_psk_client_callback);
+
+ return 0;
+}
+#endif // !OPENSSL_NO_PSK
+
+static int q_ssl_psk_use_session_callback(SSL *ssl, const EVP_MD *md, const unsigned char **id,
+ size_t *idlen, SSL_SESSION **sess)
+{
+ Q_UNUSED(ssl);
+ Q_UNUSED(md);
+ Q_UNUSED(id);
+ Q_UNUSED(idlen);
+ Q_UNUSED(sess);
+
+#ifndef OPENSSL_NO_PSK
+#ifdef QT_DEBUG
+ QSslSocketBackendPrivate *d = reinterpret_cast<QSslSocketBackendPrivate *>(q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData));
+ Q_ASSERT(d);
+ Q_ASSERT(d->mode == QSslSocket::SslClientMode);
+#endif
+
+ // Temporarily rebind the psk because it will be called next. The function will restore it.
+ q_SSL_set_psk_client_callback(ssl, &q_ssl_psk_restore_client);
+#endif
+
+ return 1; // need to return 1 or else "the connection setup fails."
+}
+#endif // TLS1_3_VERSION
+
#endif
#if QT_CONFIG(ocsp)
@@ -345,47 +398,41 @@ bool qt_OCSP_certificate_match(OCSP_SINGLERESP *singleResponse, X509 *peerCert,
#endif // ocsp
-// ### This list is shared between all threads, and protected by a
-// mutex. Investigate using thread local storage instead. Or better properly
-// use OpenSSL's ability to attach application data to an SSL/SSL_CTX
-// and extract it in a callback. See how it's done, for example, in PSK
-// callback or in DTLS verification callback.
-struct QSslErrorList
-{
- QMutex mutex;
- QVector<QSslErrorEntry> errors;
-};
-
-Q_GLOBAL_STATIC(QSslErrorList, _q_sslErrorList)
-
int q_X509Callback(int ok, X509_STORE_CTX *ctx)
{
if (!ok) {
// Store the error and at which depth the error was detected.
- _q_sslErrorList()->errors << QSslErrorEntry::fromStoreContext(ctx);
-#if !QT_CONFIG(opensslv11)
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "verification error: dumping bad certificate";
- qCDebug(lcSsl) << QSslCertificatePrivate::QSslCertificate_from_X509(q_X509_STORE_CTX_get_current_cert(ctx)).toPem();
- qCDebug(lcSsl) << "dumping chain";
- const auto certs = QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates(q_X509_STORE_CTX_get_chain(ctx));
- for (const QSslCertificate &cert : certs) {
- qCDebug(lcSsl) << "Issuer:" << "O=" << cert.issuerInfo(QSslCertificate::Organization)
- << "CN=" << cert.issuerInfo(QSslCertificate::CommonName)
- << "L=" << cert.issuerInfo(QSslCertificate::LocalityName)
- << "OU=" << cert.issuerInfo(QSslCertificate::OrganizationalUnitName)
- << "C=" << cert.issuerInfo(QSslCertificate::CountryName)
- << "ST=" << cert.issuerInfo(QSslCertificate::StateOrProvinceName);
- qCDebug(lcSsl) << "Subject:" << "O=" << cert.subjectInfo(QSslCertificate::Organization)
- << "CN=" << cert.subjectInfo(QSslCertificate::CommonName)
- << "L=" << cert.subjectInfo(QSslCertificate::LocalityName)
- << "OU=" << cert.subjectInfo(QSslCertificate::OrganizationalUnitName)
- << "C=" << cert.subjectInfo(QSslCertificate::CountryName)
- << "ST=" << cert.subjectInfo(QSslCertificate::StateOrProvinceName);
- qCDebug(lcSsl) << "Valid:" << cert.effectiveDate() << '-' << cert.expiryDate();
+
+ using ErrorListPtr = QVector<QSslErrorEntry>*;
+ ErrorListPtr errors = nullptr;
+
+ // Error list is attached to either 'SSL' or 'X509_STORE'.
+ if (X509_STORE *store = q_X509_STORE_CTX_get0_store(ctx)) { // We try store first:
+#if QT_CONFIG(opensslv11)
+ errors = ErrorListPtr(q_X509_STORE_get_ex_data(store, 0));
+#else
+ errors = ErrorListPtr(q_CRYPTO_get_ex_data(&store->ex_data, 0));
+#endif // opensslv11
}
-#endif // QSSLSOCKET_DEBUG
-#endif // !QT_CONFIG(opensslv11)
+
+ if (!errors) {
+ // Not found on store? Try SSL and its external data then. According to the OpenSSL's
+ // documentation:
+ //
+ // "Whenever a X509_STORE_CTX object is created for the verification of the peers certificate
+ // during a handshake, a pointer to the SSL object is stored into the X509_STORE_CTX object
+ // to identify the connection affected. To retrieve this pointer the X509_STORE_CTX_get_ex_data()
+ // function can be used with the correct index."
+ if (SSL *ssl = static_cast<SSL *>(q_X509_STORE_CTX_get_ex_data(ctx, q_SSL_get_ex_data_X509_STORE_CTX_idx())))
+ errors = ErrorListPtr(q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData + 1));
+ }
+
+ if (!errors) {
+ qCWarning(lcSsl, "Neither X509_STORE, nor SSL contains error list, handshake failure");
+ return 0;
+ }
+
+ errors->append(QSslErrorEntry::fromStoreContext(ctx));
}
// Always return OK to allow verification to continue. We handle the
// errors gracefully after collecting all errors, after verification has
@@ -481,7 +528,7 @@ bool QSslSocketBackendPrivate::initSslContext()
if (!sslContextPointer) {
// create a deep copy of our configuration
QSslConfigurationPrivate *configurationCopy = new QSslConfigurationPrivate(configuration);
- configurationCopy->ref.store(0); // the QSslConfiguration constructor refs up
+ configurationCopy->ref.storeRelaxed(0); // the QSslConfiguration constructor refs up
sslContextPointer = QSslContext::sharedFromConfiguration(mode, configurationCopy, allowRootCertOnDemandLoading);
}
@@ -541,11 +588,7 @@ bool QSslSocketBackendPrivate::initSslContext()
else
q_SSL_set_accept_state(ssl);
-#if OPENSSL_VERSION_NUMBER >= 0x10001000L
- // Save a pointer to this object into the SSL structure.
- if (QSslSocket::sslLibraryVersionNumber() >= 0x10001000L)
- q_SSL_set_ex_data(ssl, s_indexForSSLExtraData, this);
-#endif
+ q_SSL_set_ex_data(ssl, s_indexForSSLExtraData, this);
#if OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK)
// Set the client callback for PSK
@@ -556,6 +599,13 @@ bool QSslSocketBackendPrivate::initSslContext()
q_SSL_set_psk_server_callback(ssl, &q_ssl_psk_server_callback);
}
#endif
+#if OPENSSL_VERSION_NUMBER >= 0x10101006L
+ // Set the client callback for TLSv1.3 PSK
+ if (mode == QSslSocket::SslClientMode
+ && QSslSocket::sslLibraryBuildVersionNumber() >= 0x10101006L) {
+ q_SSL_set_psk_use_session_callback(ssl, &q_ssl_psk_use_session_callback);
+ }
+#endif // openssl version >= 0x10101006L
#if QT_CONFIG(ocsp)
if (configuration.ocspStaplingEnabled) {
@@ -1123,14 +1173,14 @@ bool QSslSocketBackendPrivate::startHandshake()
if (inSetAndEmitError)
return false;
- QMutexLocker locker(&_q_sslErrorList()->mutex);
- _q_sslErrorList()->errors.clear();
+ QVector<QSslErrorEntry> lastErrors;
+ q_SSL_set_ex_data(ssl, s_indexForSSLExtraData + 1, &lastErrors);
int result = (mode == QSslSocket::SslClientMode) ? q_SSL_connect(ssl) : q_SSL_accept(ssl);
+ q_SSL_set_ex_data(ssl, s_indexForSSLExtraData + 1, nullptr);
- const auto &lastErrors = _q_sslErrorList()->errors;
if (!lastErrors.isEmpty())
storePeerCertificates();
- for (const auto &currentError : lastErrors) {
+ for (const auto &currentError : qAsConst(lastErrors)) {
emit q->peerVerifyError(_q_OpenSSL_to_QSslError(currentError.code,
configuration.peerCertificateChain.value(currentError.depth)));
if (q->state() != QAbstractSocket::ConnectedState)
@@ -1138,7 +1188,6 @@ bool QSslSocketBackendPrivate::startHandshake()
}
errorList << lastErrors;
- locker.unlock();
// Connection aborted during handshake phase.
if (q->state() != QAbstractSocket::ConnectedState)
@@ -1520,28 +1569,14 @@ bool QSslSocketBackendPrivate::checkOcspStatus()
// 3) It checks CertID in response.
// 4) Ensures the responder is authorized to sign the status respond.
//
- // Here it's important to notice that it calls X509_cert_verify and
- // as a result, possibly, our verification callback. Given this callback
- // at the moment uses a global variable, we have to lock. This will change
- // as soon as we fix our verification procedure.
- // Also note, OpenSSL prior to 1.0.2b would only use bs->certs to
+ // Note, OpenSSL prior to 1.0.2b would only use bs->certs to
// verify the responder's chain (see their commit 4ba9a4265bd).
// Working this around - is too much fuss for ancient versions we
// are dropping quite soon anyway.
- {
- const unsigned long verificationFlags = 0;
- const QMutexLocker locker(&_q_sslErrorList()->mutex);
- // Before unlocking the mutex, startHandshake() stores errors (found in SSL_connect()
- // or SSL_accept()) into the local variable, so it's safe to clear it here - as soon
- // as we managed to lock, whoever had the lock before, already stored their own copy
- // of errors.
- _q_sslErrorList()->errors.clear();
- const int success = q_OCSP_basic_verify(basicResponse, peerChain, store, verificationFlags);
- if (success <= 0 || _q_sslErrorList()->errors.size()) {
- _q_sslErrorList()->errors.clear();
- ocspErrors.push_back(QSslError::OcspResponseCannotBeTrusted);
- }
- }
+ const unsigned long verificationFlags = 0;
+ const int success = q_OCSP_basic_verify(basicResponse, peerChain, store, verificationFlags);
+ if (success <= 0)
+ ocspErrors.push_back(QSslError::OcspResponseCannotBeTrusted);
if (q_OCSP_resp_count(basicResponse) != 1) {
ocspErrors.push_back(QSslError::OcspMalformedResponse);
@@ -1752,7 +1787,20 @@ QList<QSslError> QSslSocketBackendPrivate::verify(const QList<QSslCertificate> &
}
}
- QMutexLocker sslErrorListMutexLocker(&_q_sslErrorList()->mutex);
+ QVector<QSslErrorEntry> lastErrors;
+#if QT_CONFIG(opensslv11)
+ if (!q_X509_STORE_set_ex_data(certStore, 0, &lastErrors)) {
+ qCWarning(lcSsl) << "Unable to attach external data (error list) to a store";
+ errors << QSslError(QSslError::UnspecifiedError);
+ return errors;
+ }
+#else
+ if (!q_CRYPTO_set_ex_data(&certStore->ex_data, 0, &lastErrors)) {
+ qCWarning(lcSsl) << "Unable to attach external data (error list) to a store";
+ errors << QSslError(QSslError::UnspecifiedError);
+ return errors;
+ }
+#endif // opensslv11
// Register a custom callback to get all verification errors.
q_X509_STORE_set_verify_cb(certStore, q_X509Callback);
@@ -1802,12 +1850,7 @@ QList<QSslError> QSslSocketBackendPrivate::verify(const QList<QSslCertificate> &
q_OPENSSL_sk_free((OPENSSL_STACK *)intermediates);
// Now process the errors
- const auto errorList = std::move(_q_sslErrorList()->errors);
- _q_sslErrorList()->errors.clear();
- sslErrorListMutexLocker.unlock();
-
- // Translate the errors
if (QSslCertificatePrivate::isBlacklisted(certificateChain[0])) {
QSslError error(QSslError::CertificateBlacklisted, certificateChain[0]);
errors << error;
@@ -1821,8 +1864,8 @@ QList<QSslError> QSslSocketBackendPrivate::verify(const QList<QSslCertificate> &
}
// Translate errors from the error list into QSslErrors.
- errors.reserve(errors.size() + errorList.size());
- for (const auto &error : qAsConst(errorList))
+ errors.reserve(errors.size() + lastErrors.size());
+ for (const auto &error : qAsConst(lastErrors))
errors << _q_OpenSSL_to_QSslError(error.code, certificateChain.value(error.depth));
q_X509_STORE_free(certStore);
diff --git a/src/network/ssl/qsslsocket_openssl11_symbols_p.h b/src/network/ssl/qsslsocket_openssl11_symbols_p.h
index 9d0a14360d..0fe0899d4f 100644
--- a/src/network/ssl/qsslsocket_openssl11_symbols_p.h
+++ b/src/network/ssl/qsslsocket_openssl11_symbols_p.h
@@ -107,6 +107,8 @@ Q_AUTOTEST_EXPORT void q_X509_up_ref(X509 *a);
long q_X509_get_version(X509 *a);
EVP_PKEY *q_X509_get_pubkey(X509 *a);
void q_X509_STORE_set_verify_cb(X509_STORE *ctx, X509_STORE_CTX_verify_cb verify_cb);
+int q_X509_STORE_set_ex_data(X509_STORE *ctx, int idx, void *data);
+void *q_X509_STORE_get_ex_data(X509_STORE *r, int idx);
STACK_OF(X509) *q_X509_STORE_CTX_get0_chain(X509_STORE_CTX *ctx);
void q_DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g);
int q_DH_bits(DH *dh);
@@ -184,4 +186,10 @@ const OCSP_CERTID *q_OCSP_SINGLERESP_get0_id(const OCSP_SINGLERESP *x);
#define q_SSL_CTX_set_max_proto_version(ctx, version) \
q_SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MAX_PROTO_VERSION, version, nullptr)
+extern "C" {
+typedef int (*q_SSL_psk_use_session_cb_func_t)(SSL *, const EVP_MD *, const unsigned char **, size_t *,
+ SSL_SESSION **);
+}
+void q_SSL_set_psk_use_session_callback(SSL *s, q_SSL_psk_use_session_cb_func_t);
+
#endif
diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp
index 6f935a02e4..2eb9763e90 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols.cpp
+++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp
@@ -164,6 +164,7 @@ DEFINEFUNC(int, SSL_session_reused, SSL *a, a, return 0, return)
DEFINEFUNC2(unsigned long, SSL_CTX_set_options, SSL_CTX *ctx, ctx, unsigned long op, op, return 0, return)
#ifdef TLS1_3_VERSION
DEFINEFUNC2(int, SSL_CTX_set_ciphersuites, SSL_CTX *ctx, ctx, const char *str, str, return 0, return)
+DEFINEFUNC2(void, SSL_set_psk_use_session_callback, SSL *ssl, ssl, q_SSL_psk_use_session_cb_func_t callback, callback, return, DUMMYARG)
#endif
DEFINEFUNC3(size_t, SSL_get_client_random, SSL *a, a, unsigned char *out, out, size_t outlen, outlen, return 0, return)
DEFINEFUNC3(size_t, SSL_SESSION_get_master_key, const SSL_SESSION *ses, ses, unsigned char *out, out, size_t outlen, outlen, return 0, return)
@@ -179,6 +180,8 @@ DEFINEFUNC(ASN1_TIME *, X509_getm_notAfter, X509 *a, a, return nullptr, return)
DEFINEFUNC(long, X509_get_version, X509 *a, a, return -1, return)
DEFINEFUNC(EVP_PKEY *, X509_get_pubkey, X509 *a, a, return nullptr, return)
DEFINEFUNC2(void, X509_STORE_set_verify_cb, X509_STORE *a, a, X509_STORE_CTX_verify_cb verify_cb, verify_cb, return, DUMMYARG)
+DEFINEFUNC3(int, X509_STORE_set_ex_data, X509_STORE *a, a, int idx, idx, void *data, data, return 0, return)
+DEFINEFUNC2(void *, X509_STORE_get_ex_data, X509_STORE *r, r, int idx, idx, return nullptr, return)
DEFINEFUNC(STACK_OF(X509) *, X509_STORE_CTX_get0_chain, X509_STORE_CTX *a, a, return nullptr, return)
DEFINEFUNC3(void, CRYPTO_free, void *str, str, const char *file, file, int line, line, return, DUMMYARG)
DEFINEFUNC(long, OpenSSL_version_num, void, DUMMYARG, return 0, return)
@@ -253,6 +256,8 @@ DEFINEFUNC(int, CRYPTO_num_locks, DUMMYARG, DUMMYARG, return 0, return)
DEFINEFUNC(void, CRYPTO_set_locking_callback, void (*a)(int, int, const char *, int), a, return, DUMMYARG)
DEFINEFUNC(void, CRYPTO_set_id_callback, unsigned long (*a)(), a, return, DUMMYARG)
DEFINEFUNC(void, CRYPTO_free, void *a, a, return, DUMMYARG)
+DEFINEFUNC3(int, CRYPTO_set_ex_data, CRYPTO_EX_DATA *ad, ad, int idx, idx, void *val, val, return 0, return)
+DEFINEFUNC2(void *, CRYPTO_get_ex_data, const CRYPTO_EX_DATA *ad, ad, int idx, idx, return nullptr, return)
DEFINEFUNC(unsigned long, ERR_peek_last_error, DUMMYARG, DUMMYARG, return 0, return)
DEFINEFUNC(void, ERR_free_strings, void, DUMMYARG, return, DUMMYARG)
DEFINEFUNC(void, EVP_CIPHER_CTX_cleanup, EVP_CIPHER_CTX *a, a, return, DUMMYARG)
@@ -525,6 +530,7 @@ DEFINEFUNC2(int, X509_STORE_CTX_set_purpose, X509_STORE_CTX *a, a, int b, b, ret
DEFINEFUNC(int, X509_STORE_CTX_get_error, X509_STORE_CTX *a, a, return -1, return)
DEFINEFUNC(int, X509_STORE_CTX_get_error_depth, X509_STORE_CTX *a, a, return -1, return)
DEFINEFUNC(X509 *, X509_STORE_CTX_get_current_cert, X509_STORE_CTX *a, a, return nullptr, return)
+DEFINEFUNC(X509_STORE *, X509_STORE_CTX_get0_store, X509_STORE_CTX *ctx, ctx, return nullptr, return)
DEFINEFUNC(X509_STORE_CTX *, X509_STORE_CTX_new, DUMMYARG, DUMMYARG, return nullptr, return)
DEFINEFUNC2(void *, X509_STORE_CTX_get_ex_data, X509_STORE_CTX *ctx, ctx, int idx, idx, return nullptr, return)
DEFINEFUNC(int, SSL_get_ex_data_X509_STORE_CTX_idx, DUMMYARG, DUMMYARG, return -1, return)
@@ -975,6 +981,7 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(SSL_CTX_set_options)
#ifdef TLS1_3_VERSION
RESOLVEFUNC(SSL_CTX_set_ciphersuites)
+ RESOLVEFUNC(SSL_set_psk_use_session_callback)
#endif // TLS 1.3 or OpenSSL > 1.1.1
RESOLVEFUNC(SSL_get_client_random)
RESOLVEFUNC(SSL_SESSION_get_master_key)
@@ -992,6 +999,8 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(X509_get_version)
RESOLVEFUNC(X509_get_pubkey)
RESOLVEFUNC(X509_STORE_set_verify_cb)
+ RESOLVEFUNC(X509_STORE_set_ex_data)
+ RESOLVEFUNC(X509_STORE_get_ex_data)
RESOLVEFUNC(CRYPTO_free)
RESOLVEFUNC(OpenSSL_version_num)
RESOLVEFUNC(OpenSSL_version)
@@ -1063,6 +1072,8 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(CRYPTO_num_locks)
RESOLVEFUNC(CRYPTO_set_id_callback)
RESOLVEFUNC(CRYPTO_set_locking_callback)
+ RESOLVEFUNC(CRYPTO_set_ex_data)
+ RESOLVEFUNC(CRYPTO_get_ex_data)
RESOLVEFUNC(ERR_peek_last_error)
RESOLVEFUNC(ERR_free_strings)
RESOLVEFUNC(EVP_CIPHER_CTX_cleanup)
@@ -1324,6 +1335,7 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(X509_STORE_CTX_get_error)
RESOLVEFUNC(X509_STORE_CTX_get_error_depth)
RESOLVEFUNC(X509_STORE_CTX_get_current_cert)
+ RESOLVEFUNC(X509_STORE_CTX_get0_store)
RESOLVEFUNC(X509_cmp)
RESOLVEFUNC(X509_STORE_CTX_get_ex_data)
diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h
index fcf96dbd47..69b2b90fbd 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols_p.h
+++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h
@@ -464,6 +464,7 @@ int q_X509_STORE_CTX_set_purpose(X509_STORE_CTX *ctx, int purpose);
int q_X509_STORE_CTX_get_error(X509_STORE_CTX *ctx);
int q_X509_STORE_CTX_get_error_depth(X509_STORE_CTX *ctx);
X509 *q_X509_STORE_CTX_get_current_cert(X509_STORE_CTX *ctx);
+X509_STORE *q_X509_STORE_CTX_get0_store(X509_STORE_CTX *ctx);
// Diffie-Hellman support
DH *q_DH_new();
diff --git a/src/network/ssl/qsslsocket_opensslpre11_symbols_p.h b/src/network/ssl/qsslsocket_opensslpre11_symbols_p.h
index 46b6505346..f5626d5d16 100644
--- a/src/network/ssl/qsslsocket_opensslpre11_symbols_p.h
+++ b/src/network/ssl/qsslsocket_opensslpre11_symbols_p.h
@@ -84,6 +84,8 @@ int q_CRYPTO_num_locks();
void q_CRYPTO_set_locking_callback(void (*a)(int, int, const char *, int));
void q_CRYPTO_set_id_callback(unsigned long (*a)());
void q_CRYPTO_free(void *a);
+int q_CRYPTO_set_ex_data(CRYPTO_EX_DATA *ad, int idx, void *val);
+void *q_CRYPTO_get_ex_data(const CRYPTO_EX_DATA *ad, int idx);
unsigned long q_ERR_peek_last_error();
void q_ERR_free_strings();
void q_EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *a);
diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp
index 799e984a68..2b4af3ef9f 100644
--- a/src/opengl/qgl.cpp
+++ b/src/opengl/qgl.cpp
@@ -397,7 +397,7 @@ QGLFormat::QGLFormat(QGL::FormatOptions options, int plane)
*/
void QGLFormat::detach()
{
- if (d->ref.load() != 1) {
+ if (d->ref.loadRelaxed() != 1) {
QGLFormatPrivate *newd = new QGLFormatPrivate(d);
if (!d->ref.deref())
delete d;
@@ -5208,7 +5208,7 @@ void QGLContextGroup::addShare(const QGLContext *context, const QGLContext *shar
return;
// Make sure 'context' is not already shared with another group of contexts.
- Q_ASSERT(context->d_ptr->group->m_refs.load() == 1);
+ Q_ASSERT(context->d_ptr->group->m_refs.loadRelaxed() == 1);
// Free 'context' group resources and make it use the same resources as 'share'.
QGLContextGroup *group = share->d_ptr->group;
diff --git a/src/opengl/qglcolormap.cpp b/src/opengl/qglcolormap.cpp
index d607363ac0..f314a9715d 100644
--- a/src/opengl/qglcolormap.cpp
+++ b/src/opengl/qglcolormap.cpp
@@ -153,7 +153,7 @@ QGLColormap & QGLColormap::operator=(const QGLColormap &map)
void QGLColormap::detach_helper()
{
QGLColormapData *x = new QGLColormapData;
- x->ref.store(1);
+ x->ref.storeRelaxed(1);
x->cmapHandle = 0;
x->cells = 0;
if (d->cells) {
diff --git a/src/opengl/qglcolormap.h b/src/opengl/qglcolormap.h
index 772e327e34..b59b56e040 100644
--- a/src/opengl/qglcolormap.h
+++ b/src/opengl/qglcolormap.h
@@ -90,7 +90,7 @@ private:
inline void QGLColormap::detach()
{
- if (d->ref.load() != 1)
+ if (d->ref.loadRelaxed() != 1)
detach_helper();
}
diff --git a/src/opengl/qglframebufferobject.cpp b/src/opengl/qglframebufferobject.cpp
index b2158ebfaa..d0f82a85fa 100644
--- a/src/opengl/qglframebufferobject.cpp
+++ b/src/opengl/qglframebufferobject.cpp
@@ -145,7 +145,7 @@ extern QImage qt_gl_read_frame_buffer(const QSize&, bool, bool);
*/
void QGLFramebufferObjectFormat::detach()
{
- if (d->ref.load() != 1) {
+ if (d->ref.loadRelaxed() != 1) {
QGLFramebufferObjectFormatPrivate *newd
= new QGLFramebufferObjectFormatPrivate(d);
if (!d->ref.deref())
diff --git a/src/platformheaders/doc/src/dontdocument.qdoc b/src/platformheaders/doc/src/dontdocument.qdoc
new file mode 100644
index 0000000000..dc02c6473e
--- /dev/null
+++ b/src/platformheaders/doc/src/dontdocument.qdoc
@@ -0,0 +1,30 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \dontdocument (QMetaTypeId)
+*/
diff --git a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp
index e545d54ec2..7abf295782 100644
--- a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp
+++ b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp
@@ -721,7 +721,7 @@ QStringList QFontconfigDatabase::fallbacksForFamily(const QString &family, QFont
FcValue value;
value.type = FcTypeString;
- QByteArray cs = family.toUtf8();
+ const QByteArray cs = family.toUtf8();
value.u.s = (const FcChar8 *)cs.data();
FcPatternAdd(pattern,FC_FAMILY,value,true);
@@ -863,7 +863,7 @@ QString QFontconfigDatabase::resolveFontFamilyAlias(const QString &family) const
return family;
if (!family.isEmpty()) {
- QByteArray cs = family.toUtf8();
+ const QByteArray cs = family.toUtf8();
FcPatternAddString(pattern, FC_FAMILY, (const FcChar8 *) cs.constData());
}
FcConfigSubstitute(0, pattern, FcMatchPattern);
diff --git a/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp b/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp
index 99666bcec6..8d0a20f7b9 100644
--- a/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp
+++ b/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp
@@ -254,7 +254,7 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id,
}
newFreetype->face = face;
- newFreetype->ref.store(1);
+ newFreetype->ref.storeRelaxed(1);
newFreetype->xsize = 0;
newFreetype->ysize = 0;
newFreetype->matrix.xx = 0x10000;
diff --git a/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp
index 9d25422ca5..79f7eb3d43 100644
--- a/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp
+++ b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabase.cpp
@@ -1364,11 +1364,11 @@ QT_WARNING_POP
if (request.family != fontEngine->fontDef.family) {
qWarning("%s: Failed to load font. Got fallback instead: %s",
__FUNCTION__, qPrintable(fontEngine->fontDef.family));
- if (fontEngine->ref.load() == 0)
+ if (fontEngine->ref.loadRelaxed() == 0)
delete fontEngine;
fontEngine = 0;
} else {
- Q_ASSERT(fontEngine->ref.load() == 0);
+ Q_ASSERT(fontEngine->ref.loadRelaxed() == 0);
// Override the generated font name
switch (fontEngine->type()) {
@@ -1845,7 +1845,7 @@ LOGFONT QWindowsFontDatabase::fontDefToLOGFONT(const QFontDef &request, const QS
QString fam = faceName;
if (fam.isEmpty())
- fam = request.family;
+ fam = request.families.size() > 0 ? request.families.at(0) : request.family;
if (Q_UNLIKELY(fam.size() >= LF_FACESIZE)) {
qCritical("%s: Family name '%s' is too long.", __FUNCTION__, qPrintable(fam));
fam.truncate(LF_FACESIZE - 1);
diff --git a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp
index 02d6586fe8..bff4a2522c 100644
--- a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp
+++ b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp
@@ -98,11 +98,12 @@ QEvdevKeyboardHandler::~QEvdevKeyboardHandler()
unloadKeymap();
}
-QEvdevKeyboardHandler *QEvdevKeyboardHandler::create(const QString &device,
+std::unique_ptr<QEvdevKeyboardHandler> QEvdevKeyboardHandler::create(const QString &device,
const QString &specification,
const QString &defaultKeymapFile)
{
- qCDebug(qLcEvdevKey) << "Try to create keyboard handler for" << device << specification;
+ qCDebug(qLcEvdevKey, "Try to create keyboard handler for \"%ls\" \"%ls\"",
+ qUtf16Printable(device), qUtf16Printable(specification));
QString keymapFile = defaultKeymapFile;
int repeatDelay = 400;
@@ -127,7 +128,7 @@ QEvdevKeyboardHandler *QEvdevKeyboardHandler::create(const QString &device,
grab = arg.mid(5).toInt();
}
- qCDebug(qLcEvdevKey) << "Opening keyboard at" << device;
+ qCDebug(qLcEvdevKey, "Opening keyboard at %ls", qUtf16Printable(device));
QFdContainer fd(qt_safe_open(device.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0));
if (fd.get() >= 0) {
@@ -137,16 +138,16 @@ QEvdevKeyboardHandler *QEvdevKeyboardHandler::create(const QString &device,
::ioctl(fd.get(), EVIOCSREP, kbdrep);
}
- return new QEvdevKeyboardHandler(device, fd, disableZap, enableCompose, keymapFile);
+ return std::unique_ptr<QEvdevKeyboardHandler>(new QEvdevKeyboardHandler(device, fd, disableZap, enableCompose, keymapFile));
} else {
- qWarning("Cannot open keyboard input device '%s': %s", qPrintable(device), strerror(errno));
- return 0;
+ qErrnoWarning("Cannot open keyboard input device '%ls'", qUtf16Printable(device));
+ return nullptr;
}
}
void QEvdevKeyboardHandler::switchLed(int led, bool state)
{
- qCDebug(qLcEvdevKey) << "switchLed" << led << state;
+ qCDebug(qLcEvdevKey, "switchLed %d %d", led, int(state));
struct ::input_event led_ie;
::gettimeofday(&led_ie.time, 0);
@@ -170,7 +171,7 @@ void QEvdevKeyboardHandler::readKeycode()
return;
} else if (result < 0) {
if (errno != EINTR && errno != EAGAIN) {
- qErrnoWarning(errno, "evdevkeyboard: Could not read from input device");
+ qErrnoWarning("evdevkeyboard: Could not read from input device");
// If the device got disconnected, stop reading, otherwise we get flooded
// by the above error over and over again.
if (errno == ENODEV) {
@@ -473,7 +474,7 @@ QEvdevKeyboardHandler::KeycodeAction QEvdevKeyboardHandler::processKeycode(quint
void QEvdevKeyboardHandler::unloadKeymap()
{
- qCDebug(qLcEvdevKey) << "Unload current keymap and restore built-in";
+ qCDebug(qLcEvdevKey, "Unload current keymap and restore built-in");
if (m_keymap && m_keymap != s_keymap_default)
delete [] m_keymap;
@@ -517,12 +518,12 @@ void QEvdevKeyboardHandler::unloadKeymap()
bool QEvdevKeyboardHandler::loadKeymap(const QString &file)
{
- qCDebug(qLcEvdevKey) << "Loading keymap" << file;
+ qCDebug(qLcEvdevKey, "Loading keymap %ls", qUtf16Printable(file));
QFile f(file);
if (!f.open(QIODevice::ReadOnly)) {
- qWarning("Could not open keymap file '%s'", qPrintable(file));
+ qWarning("Could not open keymap file '%ls'", qUtf16Printable(file));
return false;
}
@@ -541,7 +542,7 @@ bool QEvdevKeyboardHandler::loadKeymap(const QString &file)
ds >> qmap_magic >> qmap_version >> qmap_keymap_size >> qmap_keycompose_size;
if (ds.status() != QDataStream::Ok || qmap_magic != QEvdevKeyboardMap::FileMagic || qmap_version != 1 || qmap_keymap_size == 0) {
- qWarning("'%s' is not a valid .qmap keymap file", qPrintable(file));
+ qWarning("'%ls' is not a valid .qmap keymap file", qUtf16Printable(file));
return false;
}
@@ -557,7 +558,7 @@ bool QEvdevKeyboardHandler::loadKeymap(const QString &file)
delete [] qmap_keymap;
delete [] qmap_keycompose;
- qWarning("Keymap file '%s' cannot be loaded.", qPrintable(file));
+ qWarning("Keymap file '%ls' cannot be loaded.", qUtf16Printable(file));
return false;
}
diff --git a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler_p.h b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler_p.h
index d154c30ed5..f92a2bf704 100644
--- a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler_p.h
+++ b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler_p.h
@@ -55,6 +55,8 @@
#include <QTimer>
#include <QDataStream>
+#include <memory>
+
QT_BEGIN_NAMESPACE
class QSocketNotifier;
@@ -168,7 +170,7 @@ public:
SwitchConsoleMask = 0x0000007f
};
- static QEvdevKeyboardHandler *create(const QString &device,
+ static std::unique_ptr<QEvdevKeyboardHandler> create(const QString &device,
const QString &specification,
const QString &defaultKeymapFile = QString());
diff --git a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp
index e1659bc0d9..52d9c34b1c 100644
--- a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp
+++ b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager.cpp
@@ -39,6 +39,8 @@
#include "qevdevkeyboardmanager_p.h"
+#include <QtInputSupport/private/qevdevutil_p.h>
+
#include <QStringList>
#include <QCoreApplication>
#include <QLoggingCategory>
@@ -61,36 +63,24 @@ QEvdevKeyboardManager::QEvdevKeyboardManager(const QString &key, const QString &
if (spec.isEmpty())
spec = specification;
- QStringList args = spec.split(QLatin1Char(':'));
- QStringList devices;
-
- foreach (const QString &arg, args) {
- if (arg.startsWith(QLatin1String("/dev/"))) {
- // if device is specified try to use it
- devices.append(arg);
- args.removeAll(arg);
- }
- }
-
- // build new specification without /dev/ elements
- m_spec = args.join(QLatin1Char(':'));
+ auto parsed = QEvdevUtil::parseSpecification(spec);
+ m_spec = std::move(parsed.spec);
// add all keyboards for devices specified in the argument list
- foreach (const QString &device, devices)
+ for (const QString &device : qAsConst(parsed.devices))
addKeyboard(device);
- if (devices.isEmpty()) {
- qCDebug(qLcEvdevKey) << "evdevkeyboard: Using device discovery";
- m_deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Keyboard, this);
- if (m_deviceDiscovery) {
+ if (parsed.devices.isEmpty()) {
+ qCDebug(qLcEvdevKey, "evdevkeyboard: Using device discovery");
+ if (auto deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Keyboard, this)) {
// scan and add already connected keyboards
- const QStringList devices = m_deviceDiscovery->scanConnectedDevices();
+ const QStringList devices = deviceDiscovery->scanConnectedDevices();
for (const QString &device : devices)
addKeyboard(device);
- connect(m_deviceDiscovery, &QDeviceDiscovery::deviceDetected,
+ connect(deviceDiscovery, &QDeviceDiscovery::deviceDetected,
this, &QEvdevKeyboardManager::addKeyboard);
- connect(m_deviceDiscovery, &QDeviceDiscovery::deviceRemoved,
+ connect(deviceDiscovery, &QDeviceDiscovery::deviceRemoved,
this, &QEvdevKeyboardManager::removeKeyboard);
}
}
@@ -98,36 +88,34 @@ QEvdevKeyboardManager::QEvdevKeyboardManager(const QString &key, const QString &
QEvdevKeyboardManager::~QEvdevKeyboardManager()
{
- qDeleteAll(m_keyboards);
- m_keyboards.clear();
}
void QEvdevKeyboardManager::addKeyboard(const QString &deviceNode)
{
- qCDebug(qLcEvdevKey) << "Adding keyboard at" << deviceNode;
- QEvdevKeyboardHandler *keyboard;
- keyboard = QEvdevKeyboardHandler::create(deviceNode, m_spec, m_defaultKeymapFile);
+ qCDebug(qLcEvdevKey, "Adding keyboard at %ls", qUtf16Printable(deviceNode));
+ auto keyboard = QEvdevKeyboardHandler::create(deviceNode, m_spec, m_defaultKeymapFile);
if (keyboard) {
- m_keyboards.insert(deviceNode, keyboard);
- QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount(
- QInputDeviceManager::DeviceTypeKeyboard, m_keyboards.count());
+ m_keyboards.add(deviceNode, std::move(keyboard));
+ updateDeviceCount();
} else {
- qWarning("Failed to open keyboard device %s", qPrintable(deviceNode));
+ qWarning("Failed to open keyboard device %ls", qUtf16Printable(deviceNode));
}
}
void QEvdevKeyboardManager::removeKeyboard(const QString &deviceNode)
{
- if (m_keyboards.contains(deviceNode)) {
- qCDebug(qLcEvdevKey) << "Removing keyboard at" << deviceNode;
- QEvdevKeyboardHandler *keyboard = m_keyboards.value(deviceNode);
- m_keyboards.remove(deviceNode);
- QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount(
- QInputDeviceManager::DeviceTypeKeyboard, m_keyboards.count());
- delete keyboard;
+ if (m_keyboards.remove(deviceNode)) {
+ qCDebug(qLcEvdevKey, "Removing keyboard at %ls", qUtf16Printable(deviceNode));
+ updateDeviceCount();
}
}
+void QEvdevKeyboardManager::updateDeviceCount()
+{
+ QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount(
+ QInputDeviceManager::DeviceTypeKeyboard, m_keyboards.count());
+}
+
void QEvdevKeyboardManager::loadKeymap(const QString &file)
{
m_defaultKeymapFile = file;
@@ -141,22 +129,22 @@ void QEvdevKeyboardManager::loadKeymap(const QString &file)
if (arg.startsWith(QLatin1String("keymap=")))
keymapFromSpec = arg.mid(7).toString();
}
- foreach (QEvdevKeyboardHandler *handler, m_keyboards) {
+ for (const auto &keyboard : m_keyboards) {
if (keymapFromSpec.isEmpty())
- handler->unloadKeymap();
+ keyboard.handler->unloadKeymap();
else
- handler->loadKeymap(keymapFromSpec);
+ keyboard.handler->loadKeymap(keymapFromSpec);
}
} else {
- foreach (QEvdevKeyboardHandler *handler, m_keyboards)
- handler->loadKeymap(file);
+ for (const auto &keyboard : m_keyboards)
+ keyboard.handler->loadKeymap(file);
}
}
void QEvdevKeyboardManager::switchLang()
{
- foreach (QEvdevKeyboardHandler *handler, m_keyboards)
- handler->switchLang();
+ for (const auto &keyboard : m_keyboards)
+ keyboard.handler->switchLang();
}
QT_END_NAMESPACE
diff --git a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager_p.h b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager_p.h
index 01b7e9fc0e..d91da330c3 100644
--- a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager_p.h
+++ b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardmanager_p.h
@@ -53,6 +53,7 @@
#include "qevdevkeyboardhandler_p.h"
+#include <QtInputSupport/private/devicehandlerlist_p.h>
#include <QtDeviceDiscoverySupport/private/qdevicediscovery_p.h>
#include <QObject>
@@ -74,9 +75,10 @@ public:
void removeKeyboard(const QString &deviceNode);
private:
+ void updateDeviceCount();
+
QString m_spec;
- QHash<QString,QEvdevKeyboardHandler*> m_keyboards;
- QDeviceDiscovery *m_deviceDiscovery;
+ QtInputSupport::DeviceHandlerList<QEvdevKeyboardHandler> m_keyboards;
QString m_defaultKeymapFile;
};
diff --git a/src/platformsupport/input/evdevmouse/qevdevmousehandler.cpp b/src/platformsupport/input/evdevmouse/qevdevmousehandler.cpp
index 86a4cd0076..6a53ad2088 100644
--- a/src/platformsupport/input/evdevmouse/qevdevmousehandler.cpp
+++ b/src/platformsupport/input/evdevmouse/qevdevmousehandler.cpp
@@ -66,7 +66,7 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(qLcEvdevMouse, "qt.qpa.input")
-QEvdevMouseHandler *QEvdevMouseHandler::create(const QString &device, const QString &specification)
+std::unique_ptr<QEvdevMouseHandler> QEvdevMouseHandler::create(const QString &device, const QString &specification)
{
qCDebug(qLcEvdevMouse) << "create mouse handler for" << device << specification;
@@ -91,10 +91,10 @@ QEvdevMouseHandler *QEvdevMouseHandler::create(const QString &device, const QStr
fd = qt_safe_open(device.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0);
if (fd >= 0) {
::ioctl(fd, EVIOCGRAB, grab);
- return new QEvdevMouseHandler(device, fd, abs, compression, jitterLimit);
+ return std::unique_ptr<QEvdevMouseHandler>(new QEvdevMouseHandler(device, fd, abs, compression, jitterLimit));
} else {
qErrnoWarning(errno, "Cannot open mouse input device %s", qPrintable(device));
- return 0;
+ return nullptr;
}
}
diff --git a/src/platformsupport/input/evdevmouse/qevdevmousehandler_p.h b/src/platformsupport/input/evdevmouse/qevdevmousehandler_p.h
index c7f2b04eb2..727f1a02f9 100644
--- a/src/platformsupport/input/evdevmouse/qevdevmousehandler_p.h
+++ b/src/platformsupport/input/evdevmouse/qevdevmousehandler_p.h
@@ -56,6 +56,8 @@
#include <QPoint>
#include <QEvent>
+#include <memory>
+
QT_BEGIN_NAMESPACE
class QSocketNotifier;
@@ -64,7 +66,7 @@ class QEvdevMouseHandler : public QObject
{
Q_OBJECT
public:
- static QEvdevMouseHandler *create(const QString &device, const QString &specification);
+ static std::unique_ptr<QEvdevMouseHandler> create(const QString &device, const QString &specification);
~QEvdevMouseHandler();
void readMouseData();
diff --git a/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp b/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp
index ae81bca00f..0c19294905 100644
--- a/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp
+++ b/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp
@@ -39,6 +39,8 @@
#include "qevdevmousemanager_p.h"
+#include <QtInputSupport/private/qevdevutil_p.h>
+
#include <QStringList>
#include <QGuiApplication>
#include <QScreen>
@@ -63,40 +65,32 @@ QEvdevMouseManager::QEvdevMouseManager(const QString &key, const QString &specif
if (spec.isEmpty())
spec = specification;
- QStringList args = spec.split(QLatin1Char(':'));
- QStringList devices;
+ auto parsed = QEvdevUtil::parseSpecification(spec);
+ m_spec = std::move(parsed.spec);
- foreach (const QString &arg, args) {
- if (arg.startsWith(QLatin1String("/dev/"))) {
- // if device is specified try to use it
- devices.append(arg);
- args.removeAll(arg);
- } else if (arg.startsWith(QLatin1String("xoffset="))) {
+ for (const QStringRef &arg : qAsConst(parsed.args)) {
+ if (arg.startsWith(QLatin1String("xoffset="))) {
m_xoffset = arg.mid(8).toInt();
} else if (arg.startsWith(QLatin1String("yoffset="))) {
m_yoffset = arg.mid(8).toInt();
}
}
- // build new specification without /dev/ elements
- m_spec = args.join(QLatin1Char(':'));
-
// add all mice for devices specified in the argument list
- foreach (const QString &device, devices)
+ for (const QString &device : qAsConst(parsed.devices))
addMouse(device);
- if (devices.isEmpty()) {
- qCDebug(qLcEvdevMouse) << "evdevmouse: Using device discovery";
- m_deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Mouse | QDeviceDiscovery::Device_Touchpad, this);
- if (m_deviceDiscovery) {
+ if (parsed.devices.isEmpty()) {
+ qCDebug(qLcEvdevMouse, "evdevmouse: Using device discovery");
+ if (auto deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Mouse | QDeviceDiscovery::Device_Touchpad, this)) {
// scan and add already connected keyboards
- const QStringList devices = m_deviceDiscovery->scanConnectedDevices();
+ const QStringList devices = deviceDiscovery->scanConnectedDevices();
for (const QString &device : devices)
addMouse(device);
- connect(m_deviceDiscovery, &QDeviceDiscovery::deviceDetected,
+ connect(deviceDiscovery, &QDeviceDiscovery::deviceDetected,
this, &QEvdevMouseManager::addMouse);
- connect(m_deviceDiscovery, &QDeviceDiscovery::deviceRemoved,
+ connect(deviceDiscovery, &QDeviceDiscovery::deviceRemoved,
this, &QEvdevMouseManager::removeMouse);
}
}
@@ -111,8 +105,6 @@ QEvdevMouseManager::QEvdevMouseManager(const QString &key, const QString &specif
QEvdevMouseManager::~QEvdevMouseManager()
{
- qDeleteAll(m_mice);
- m_mice.clear();
}
void QEvdevMouseManager::clampPosition()
@@ -159,31 +151,32 @@ void QEvdevMouseManager::handleWheelEvent(QPoint delta)
void QEvdevMouseManager::addMouse(const QString &deviceNode)
{
- qCDebug(qLcEvdevMouse) << "Adding mouse at" << deviceNode;
- QEvdevMouseHandler *handler = QEvdevMouseHandler::create(deviceNode, m_spec);
+ qCDebug(qLcEvdevMouse, "Adding mouse at %ls", qUtf16Printable(deviceNode));
+ auto handler = QEvdevMouseHandler::create(deviceNode, m_spec);
if (handler) {
- connect(handler, &QEvdevMouseHandler::handleMouseEvent,
+ connect(handler.get(), &QEvdevMouseHandler::handleMouseEvent,
this, &QEvdevMouseManager::handleMouseEvent);
- connect(handler, &QEvdevMouseHandler::handleWheelEvent,
+ connect(handler.get(), &QEvdevMouseHandler::handleWheelEvent,
this, &QEvdevMouseManager::handleWheelEvent);
- m_mice.insert(deviceNode, handler);
- QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount(
- QInputDeviceManager::DeviceTypePointer, m_mice.count());
+ m_mice.add(deviceNode, std::move(handler));
+ updateDeviceCount();
} else {
- qWarning("evdevmouse: Failed to open mouse device %s", qPrintable(deviceNode));
+ qWarning("evdevmouse: Failed to open mouse device %ls", qUtf16Printable(deviceNode));
}
}
void QEvdevMouseManager::removeMouse(const QString &deviceNode)
{
- if (m_mice.contains(deviceNode)) {
- qCDebug(qLcEvdevMouse) << "Removing mouse at" << deviceNode;
- QEvdevMouseHandler *handler = m_mice.value(deviceNode);
- m_mice.remove(deviceNode);
- QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount(
- QInputDeviceManager::DeviceTypePointer, m_mice.count());
- delete handler;
+ if (m_mice.remove(deviceNode)) {
+ qCDebug(qLcEvdevMouse, "Removing mouse at %ls", qUtf16Printable(deviceNode));
+ updateDeviceCount();
}
}
+void QEvdevMouseManager::updateDeviceCount()
+{
+ QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount(
+ QInputDeviceManager::DeviceTypePointer, m_mice.count());
+}
+
QT_END_NAMESPACE
diff --git a/src/platformsupport/input/evdevmouse/qevdevmousemanager_p.h b/src/platformsupport/input/evdevmouse/qevdevmousemanager_p.h
index c63ca29a71..f5c32ed8b5 100644
--- a/src/platformsupport/input/evdevmouse/qevdevmousemanager_p.h
+++ b/src/platformsupport/input/evdevmouse/qevdevmousemanager_p.h
@@ -53,6 +53,8 @@
#include "qevdevmousehandler_p.h"
+#include <QtInputSupport/private/devicehandlerlist_p.h>
+
#include <QObject>
#include <QHash>
#include <QSocketNotifier>
@@ -77,10 +79,10 @@ public:
private:
void clampPosition();
+ void updateDeviceCount();
QString m_spec;
- QHash<QString,QEvdevMouseHandler*> m_mice;
- QDeviceDiscovery *m_deviceDiscovery;
+ QtInputSupport::DeviceHandlerList<QEvdevMouseHandler> m_mice;
int m_x;
int m_y;
int m_xoffset;
diff --git a/src/platformsupport/input/evdevtablet/qevdevtablethandler.cpp b/src/platformsupport/input/evdevtablet/qevdevtablethandler.cpp
index b6051aaf3c..c86840b76c 100644
--- a/src/platformsupport/input/evdevtablet/qevdevtablethandler.cpp
+++ b/src/platformsupport/input/evdevtablet/qevdevtablethandler.cpp
@@ -172,11 +172,11 @@ QEvdevTabletHandler::QEvdevTabletHandler(const QString &device, const QString &s
setObjectName(QLatin1String("Evdev Tablet Handler"));
- qCDebug(qLcEvdevTablet, "evdevtablet: using %s", qPrintable(device));
+ qCDebug(qLcEvdevTablet, "evdevtablet: using %ls", qUtf16Printable(device));
m_fd = QT_OPEN(device.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0);
if (m_fd < 0) {
- qErrnoWarning(errno, "evdevtablet: Cannot open input device %s", qPrintable(device));
+ qErrnoWarning("evdevtablet: Cannot open input device %ls", qUtf16Printable(device));
return;
}
@@ -184,11 +184,11 @@ QEvdevTabletHandler::QEvdevTabletHandler(const QString &device, const QString &s
if (grabSuccess)
ioctl(m_fd, EVIOCGRAB, (void *) 0);
else
- qWarning("evdevtablet: %s: The device is grabbed by another process. No events will be read.", qPrintable(device));
+ qWarning("evdevtablet: %ls: The device is grabbed by another process. No events will be read.", qUtf16Printable(device));
d = new QEvdevTabletData(this);
if (!queryLimits())
- qWarning("evdevtablet: %s: Unset or invalid ABS limits. Behavior will be unspecified.", qPrintable(device));
+ qWarning("evdevtablet: %ls: Unset or invalid ABS limits. Behavior will be unspecified.", qUtf16Printable(device));
m_notifier = new QSocketNotifier(m_fd, QSocketNotifier::Read, this);
connect(m_notifier, &QSocketNotifier::activated, this, &QEvdevTabletHandler::readData);
@@ -216,32 +216,32 @@ bool QEvdevTabletHandler::queryLimits()
if (ok) {
d->minValues.x = absInfo.minimum;
d->maxValues.x = absInfo.maximum;
- qCDebug(qLcEvdevTablet, "evdevtablet: %s: min X: %d max X: %d", qPrintable(m_device),
+ qCDebug(qLcEvdevTablet, "evdevtablet: %ls: min X: %d max X: %d", qUtf16Printable(m_device),
d->minValues.x, d->maxValues.x);
}
ok &= ioctl(m_fd, EVIOCGABS(ABS_Y), &absInfo) >= 0;
if (ok) {
d->minValues.y = absInfo.minimum;
d->maxValues.y = absInfo.maximum;
- qCDebug(qLcEvdevTablet, "evdevtablet: %s: min Y: %d max Y: %d", qPrintable(m_device),
+ qCDebug(qLcEvdevTablet, "evdevtablet: %ls: min Y: %d max Y: %d", qUtf16Printable(m_device),
d->minValues.y, d->maxValues.y);
}
if (ioctl(m_fd, EVIOCGABS(ABS_PRESSURE), &absInfo) >= 0) {
d->minValues.p = absInfo.minimum;
d->maxValues.p = absInfo.maximum;
- qCDebug(qLcEvdevTablet, "evdevtablet: %s: min pressure: %d max pressure: %d", qPrintable(m_device),
+ qCDebug(qLcEvdevTablet, "evdevtablet: %ls: min pressure: %d max pressure: %d", qUtf16Printable(m_device),
d->minValues.p, d->maxValues.p);
}
if (ioctl(m_fd, EVIOCGABS(ABS_DISTANCE), &absInfo) >= 0) {
d->minValues.d = absInfo.minimum;
d->maxValues.d = absInfo.maximum;
- qCDebug(qLcEvdevTablet, "evdevtablet: %s: min distance: %d max distance: %d", qPrintable(m_device),
+ qCDebug(qLcEvdevTablet, "evdevtablet: %ls: min distance: %d max distance: %d", qUtf16Printable(m_device),
d->minValues.d, d->maxValues.d);
}
char name[128];
if (ioctl(m_fd, EVIOCGNAME(sizeof(name) - 1), name) >= 0) {
d->devName = QString::fromLocal8Bit(name);
- qCDebug(qLcEvdevTablet, "evdevtablet: %s: device name: %s", qPrintable(m_device), name);
+ qCDebug(qLcEvdevTablet, "evdevtablet: %ls: device name: %s", qUtf16Printable(m_device), name);
}
return ok;
}
@@ -253,11 +253,11 @@ void QEvdevTabletHandler::readData()
for (; ;) {
int result = QT_READ(m_fd, reinterpret_cast<char*>(buffer) + n, sizeof(buffer) - n);
if (!result) {
- qWarning("evdevtablet: %s: Got EOF from input device", qPrintable(m_device));
+ qWarning("evdevtablet: %ls: Got EOF from input device", qUtf16Printable(m_device));
return;
} else if (result < 0) {
if (errno != EINTR && errno != EAGAIN) {
- qErrnoWarning(errno, "evdevtablet: %s: Could not read from input device", qPrintable(m_device));
+ qErrnoWarning("evdevtablet: %ls: Could not read from input device", qUtf16Printable(m_device));
if (errno == ENODEV) { // device got disconnected -> stop reading
delete m_notifier;
m_notifier = 0;
diff --git a/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp b/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp
index 90949408ac..d9888c5b97 100644
--- a/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp
+++ b/src/platformsupport/input/evdevtablet/qevdevtabletmanager.cpp
@@ -40,12 +40,15 @@
#include "qevdevtabletmanager_p.h"
#include "qevdevtablethandler_p.h"
+#include <QtInputSupport/private/qevdevutil_p.h>
+
#include <QStringList>
#include <QGuiApplication>
#include <QLoggingCategory>
#include <QtDeviceDiscoverySupport/private/qdevicediscovery_p.h>
#include <private/qguiapplication_p.h>
#include <private/qinputdevicemanager_p_p.h>
+#include <private/qmemory_p.h>
QT_BEGIN_NAMESPACE
@@ -64,34 +67,23 @@ QEvdevTabletManager::QEvdevTabletManager(const QString &key, const QString &spec
if (spec.isEmpty())
spec = specification;
- QStringList args = spec.split(QLatin1Char(':'));
- QStringList devices;
-
- foreach (const QString &arg, args) {
- if (arg.startsWith(QLatin1String("/dev/"))) {
- devices.append(arg);
- args.removeAll(arg);
- }
- }
-
- // build new specification without /dev/ elements
- m_spec = args.join(QLatin1Char(':'));
+ auto parsed = QEvdevUtil::parseSpecification(spec);
+ m_spec = std::move(parsed.spec);
- foreach (const QString &device, devices)
+ for (const QString &device : qAsConst(parsed.devices))
addDevice(device);
// when no devices specified, use device discovery to scan and monitor
- if (devices.isEmpty()) {
- qCDebug(qLcEvdevTablet) << "evdevtablet: Using device discovery";
- m_deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Tablet, this);
- if (m_deviceDiscovery) {
- const QStringList devices = m_deviceDiscovery->scanConnectedDevices();
+ if (parsed.devices.isEmpty()) {
+ qCDebug(qLcEvdevTablet, "evdevtablet: Using device discovery");
+ if (auto deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Tablet, this)) {
+ const QStringList devices = deviceDiscovery->scanConnectedDevices();
for (const QString &device : devices)
addDevice(device);
- connect(m_deviceDiscovery, &QDeviceDiscovery::deviceDetected,
+ connect(deviceDiscovery, &QDeviceDiscovery::deviceDetected,
this, &QEvdevTabletManager::addDevice);
- connect(m_deviceDiscovery, &QDeviceDiscovery::deviceRemoved,
+ connect(deviceDiscovery, &QDeviceDiscovery::deviceRemoved,
this, &QEvdevTabletManager::removeDevice);
}
}
@@ -99,33 +91,32 @@ QEvdevTabletManager::QEvdevTabletManager(const QString &key, const QString &spec
QEvdevTabletManager::~QEvdevTabletManager()
{
- qDeleteAll(m_activeDevices);
}
void QEvdevTabletManager::addDevice(const QString &deviceNode)
{
- qCDebug(qLcEvdevTablet) << "Adding device at" << deviceNode;
- QEvdevTabletHandlerThread *handler;
- handler = new QEvdevTabletHandlerThread(deviceNode, m_spec);
+ qCDebug(qLcEvdevTablet, "Adding device at %ls", qUtf16Printable(deviceNode));
+ auto handler = qt_make_unique<QEvdevTabletHandlerThread>(deviceNode, m_spec);
if (handler) {
- m_activeDevices.insert(deviceNode, handler);
- QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount(
- QInputDeviceManager::DeviceTypeTablet, m_activeDevices.count());
+ m_activeDevices.add(deviceNode, std::move(handler));
+ updateDeviceCount();
} else {
- qWarning("evdevtablet: Failed to open tablet device %s", qPrintable(deviceNode));
+ qWarning("evdevtablet: Failed to open tablet device %ls", qUtf16Printable(deviceNode));
}
}
void QEvdevTabletManager::removeDevice(const QString &deviceNode)
{
- if (m_activeDevices.contains(deviceNode)) {
- qCDebug(qLcEvdevTablet) << "Removing device at" << deviceNode;
- QEvdevTabletHandlerThread *handler = m_activeDevices.value(deviceNode);
- m_activeDevices.remove(deviceNode);
- QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount(
- QInputDeviceManager::DeviceTypeTablet, m_activeDevices.count());
- delete handler;
+ if (m_activeDevices.remove(deviceNode)) {
+ qCDebug(qLcEvdevTablet, "Removing device at %ls", qUtf16Printable(deviceNode));
+ updateDeviceCount();
}
}
+void QEvdevTabletManager::updateDeviceCount()
+{
+ QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount(
+ QInputDeviceManager::DeviceTypeTablet, m_activeDevices.count());
+}
+
QT_END_NAMESPACE
diff --git a/src/platformsupport/input/evdevtablet/qevdevtabletmanager_p.h b/src/platformsupport/input/evdevtablet/qevdevtabletmanager_p.h
index b598156e52..bb18ffba04 100644
--- a/src/platformsupport/input/evdevtablet/qevdevtabletmanager_p.h
+++ b/src/platformsupport/input/evdevtablet/qevdevtabletmanager_p.h
@@ -51,6 +51,8 @@
// We mean it.
//
+#include <QtInputSupport/private/devicehandlerlist_p.h>
+
#include <QObject>
#include <QHash>
#include <QSocketNotifier>
@@ -70,9 +72,10 @@ public:
void removeDevice(const QString &deviceNode);
private:
+ void updateDeviceCount();
+
QString m_spec;
- QDeviceDiscovery *m_deviceDiscovery;
- QHash<QString, QEvdevTabletHandlerThread *> m_activeDevices;
+ QtInputSupport::DeviceHandlerList<QEvdevTabletHandlerThread> m_activeDevices;
};
QT_END_NAMESPACE
diff --git a/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp b/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp
index c3a5391255..737d85d5c3 100644
--- a/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp
+++ b/src/platformsupport/input/evdevtouch/qevdevtouchhandler.cpp
@@ -228,7 +228,7 @@ QEvdevTouchScreenHandler::QEvdevTouchScreenHandler(const QString &device, const
}
}
- qCDebug(qLcEvdevTouch, "evdevtouch: Using device %s", qPrintable(device));
+ qCDebug(qLcEvdevTouch, "evdevtouch: Using device %ls", qUtf16Printable(device));
m_fd = QT_OPEN(device.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0);
@@ -236,7 +236,7 @@ QEvdevTouchScreenHandler::QEvdevTouchScreenHandler(const QString &device, const
m_notify = new QSocketNotifier(m_fd, QSocketNotifier::Read, this);
connect(m_notify, &QSocketNotifier::activated, this, &QEvdevTouchScreenHandler::readData);
} else {
- qErrnoWarning(errno, "evdevtouch: Cannot open input device %s", qPrintable(device));
+ qErrnoWarning("evdevtouch: Cannot open input device %ls", qUtf16Printable(device));
return;
}
@@ -266,8 +266,8 @@ QEvdevTouchScreenHandler::QEvdevTouchScreenHandler(const QString &device, const
d->deviceNode = device;
qCDebug(qLcEvdevTouch,
- "evdevtouch: %s: Protocol type %c %s (%s), filtered=%s",
- qPrintable(d->deviceNode),
+ "evdevtouch: %ls: Protocol type %c %s (%s), filtered=%s",
+ qUtf16Printable(d->deviceNode),
d->m_typeB ? 'B' : 'A', mtdevStr,
d->m_singleTouch ? "single" : "multi",
d->m_filtered ? "yes" : "no");
@@ -279,7 +279,7 @@ QEvdevTouchScreenHandler::QEvdevTouchScreenHandler(const QString &device, const
bool has_x_range = false, has_y_range = false;
if (ioctl(m_fd, EVIOCGABS((d->m_singleTouch ? ABS_X : ABS_MT_POSITION_X)), &absInfo) >= 0) {
- qCDebug(qLcEvdevTouch, "evdevtouch: %s: min X: %d max X: %d", qPrintable(device),
+ qCDebug(qLcEvdevTouch, "evdevtouch: %ls: min X: %d max X: %d", qUtf16Printable(device),
absInfo.minimum, absInfo.maximum);
d->hw_range_x_min = absInfo.minimum;
d->hw_range_x_max = absInfo.maximum;
@@ -287,7 +287,7 @@ QEvdevTouchScreenHandler::QEvdevTouchScreenHandler(const QString &device, const
}
if (ioctl(m_fd, EVIOCGABS((d->m_singleTouch ? ABS_Y : ABS_MT_POSITION_Y)), &absInfo) >= 0) {
- qCDebug(qLcEvdevTouch, "evdevtouch: %s: min Y: %d max Y: %d", qPrintable(device),
+ qCDebug(qLcEvdevTouch, "evdevtouch: %ls: min Y: %d max Y: %d", qUtf16Printable(device),
absInfo.minimum, absInfo.maximum);
d->hw_range_y_min = absInfo.minimum;
d->hw_range_y_max = absInfo.maximum;
@@ -295,10 +295,10 @@ QEvdevTouchScreenHandler::QEvdevTouchScreenHandler(const QString &device, const
}
if (!has_x_range || !has_y_range)
- qWarning("evdevtouch: %s: Invalid ABS limits, behavior unspecified", qPrintable(device));
+ qWarning("evdevtouch: %ls: Invalid ABS limits, behavior unspecified", qUtf16Printable(device));
if (ioctl(m_fd, EVIOCGABS(ABS_PRESSURE), &absInfo) >= 0) {
- qCDebug(qLcEvdevTouch, "evdevtouch: %s: min pressure: %d max pressure: %d", qPrintable(device),
+ qCDebug(qLcEvdevTouch, "evdevtouch: %ls: min pressure: %d max pressure: %d", qUtf16Printable(device),
absInfo.minimum, absInfo.maximum);
if (absInfo.maximum > absInfo.minimum) {
d->hw_pressure_min = absInfo.minimum;
@@ -309,7 +309,7 @@ QEvdevTouchScreenHandler::QEvdevTouchScreenHandler(const QString &device, const
char name[1024];
if (ioctl(m_fd, EVIOCGNAME(sizeof(name) - 1), name) >= 0) {
d->hw_name = QString::fromLocal8Bit(name);
- qCDebug(qLcEvdevTouch, "evdevtouch: %s: device name: %s", qPrintable(device), name);
+ qCDebug(qLcEvdevTouch, "evdevtouch: %ls: device name: %s", qUtf16Printable(device), name);
}
// Fix up the coordinate ranges for am335x in case the kernel driver does not have them fixed.
@@ -345,8 +345,8 @@ QEvdevTouchScreenHandler::QEvdevTouchScreenHandler(const QString &device, const
if (mapping.load()) {
d->m_screenName = mapping.screenNameForDeviceNode(d->deviceNode);
if (!d->m_screenName.isEmpty())
- qCDebug(qLcEvdevTouch, "evdevtouch: Mapping device %s to screen %s",
- qPrintable(d->deviceNode), qPrintable(d->m_screenName));
+ qCDebug(qLcEvdevTouch, "evdevtouch: Mapping device %ls to screen %ls",
+ qUtf16Printable(d->deviceNode), qUtf16Printable(d->m_screenName));
}
registerTouchDevice();
@@ -427,7 +427,7 @@ err:
return;
} else if (events < 0) {
if (errno != EINTR && errno != EAGAIN) {
- qErrnoWarning(errno, "evdevtouch: Could not read from input device");
+ qErrnoWarning("evdevtouch: Could not read from input device");
if (errno == ENODEV) { // device got disconnected -> stop reading
delete m_notify;
m_notify = nullptr;
diff --git a/src/platformsupport/input/evdevtouch/qevdevtouchmanager.cpp b/src/platformsupport/input/evdevtouch/qevdevtouchmanager.cpp
index 4cacbf03e5..b280f27fac 100644
--- a/src/platformsupport/input/evdevtouch/qevdevtouchmanager.cpp
+++ b/src/platformsupport/input/evdevtouch/qevdevtouchmanager.cpp
@@ -40,12 +40,15 @@
#include "qevdevtouchmanager_p.h"
#include "qevdevtouchhandler_p.h"
+#include <QtInputSupport/private/qevdevutil_p.h>
+
#include <QStringList>
#include <QGuiApplication>
#include <QLoggingCategory>
#include <QtDeviceDiscoverySupport/private/qdevicediscovery_p.h>
#include <private/qguiapplication_p.h>
#include <private/qinputdevicemanager_p_p.h>
+#include <private/qmemory_p.h>
QT_BEGIN_NAMESPACE
@@ -64,34 +67,23 @@ QEvdevTouchManager::QEvdevTouchManager(const QString &key, const QString &specif
if (spec.isEmpty())
spec = specification;
- QStringList args = spec.split(QLatin1Char(':'));
- QStringList devices;
-
- foreach (const QString &arg, args) {
- if (arg.startsWith(QLatin1String("/dev/"))) {
- devices.append(arg);
- args.removeAll(arg);
- }
- }
-
- // build new specification without /dev/ elements
- m_spec = args.join(QLatin1Char(':'));
+ auto parsed = QEvdevUtil::parseSpecification(spec);
+ m_spec = std::move(parsed.spec);
- foreach (const QString &device, devices)
+ for (const QString &device : qAsConst(parsed.devices))
addDevice(device);
// when no devices specified, use device discovery to scan and monitor
- if (devices.isEmpty()) {
- qCDebug(qLcEvdevTouch) << "evdevtouch: Using device discovery";
- m_deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Touchpad | QDeviceDiscovery::Device_Touchscreen, this);
- if (m_deviceDiscovery) {
- const QStringList devices = m_deviceDiscovery->scanConnectedDevices();
+ if (parsed.devices.isEmpty()) {
+ qCDebug(qLcEvdevTouch, "evdevtouch: Using device discovery");
+ if (auto deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Touchpad | QDeviceDiscovery::Device_Touchscreen, this)) {
+ const QStringList devices = deviceDiscovery->scanConnectedDevices();
for (const QString &device : devices)
addDevice(device);
- connect(m_deviceDiscovery, &QDeviceDiscovery::deviceDetected,
+ connect(deviceDiscovery, &QDeviceDiscovery::deviceDetected,
this, &QEvdevTouchManager::addDevice);
- connect(m_deviceDiscovery, &QDeviceDiscovery::deviceRemoved,
+ connect(deviceDiscovery, &QDeviceDiscovery::deviceRemoved,
this, &QEvdevTouchManager::removeDevice);
}
}
@@ -99,30 +91,24 @@ QEvdevTouchManager::QEvdevTouchManager(const QString &key, const QString &specif
QEvdevTouchManager::~QEvdevTouchManager()
{
- qDeleteAll(m_activeDevices);
}
void QEvdevTouchManager::addDevice(const QString &deviceNode)
{
- qCDebug(qLcEvdevTouch) << "evdevtouch: Adding device at" << deviceNode;
- QEvdevTouchScreenHandlerThread *handler;
- handler = new QEvdevTouchScreenHandlerThread(deviceNode, m_spec);
+ qCDebug(qLcEvdevTouch, "evdevtouch: Adding device at %ls", qUtf16Printable(deviceNode));
+ auto handler = qt_make_unique<QEvdevTouchScreenHandlerThread>(deviceNode, m_spec);
if (handler) {
- m_activeDevices.insert(deviceNode, handler);
- connect(handler, &QEvdevTouchScreenHandlerThread::touchDeviceRegistered, this, &QEvdevTouchManager::updateInputDeviceCount);
+ m_activeDevices.add(deviceNode, std::move(handler));
+ connect(handler.get(), &QEvdevTouchScreenHandlerThread::touchDeviceRegistered, this, &QEvdevTouchManager::updateInputDeviceCount);
} else {
- qWarning("evdevtouch: Failed to open touch device %s", qPrintable(deviceNode));
+ qWarning("evdevtouch: Failed to open touch device %ls", qUtf16Printable(deviceNode));
}
}
void QEvdevTouchManager::removeDevice(const QString &deviceNode)
{
- if (m_activeDevices.contains(deviceNode)) {
- qCDebug(qLcEvdevTouch) << "evdevtouch: Removing device at" << deviceNode;
- QEvdevTouchScreenHandlerThread *handler = m_activeDevices.value(deviceNode);
- m_activeDevices.remove(deviceNode);
- delete handler;
-
+ if (m_activeDevices.remove(deviceNode)) {
+ qCDebug(qLcEvdevTouch, "evdevtouch: Removing device at %ls", qUtf16Printable(deviceNode));
updateInputDeviceCount();
}
}
@@ -130,13 +116,13 @@ void QEvdevTouchManager::removeDevice(const QString &deviceNode)
void QEvdevTouchManager::updateInputDeviceCount()
{
int registeredTouchDevices = 0;
- Q_FOREACH (QEvdevTouchScreenHandlerThread *handler, m_activeDevices) {
- if (handler->isTouchDeviceRegistered())
+ for (const auto &device : m_activeDevices) {
+ if (device.handler->isTouchDeviceRegistered())
++registeredTouchDevices;
}
- qCDebug(qLcEvdevTouch) << "evdevtouch: Updating QInputDeviceManager device count:" << registeredTouchDevices << " touch devices,"
- << m_activeDevices.count() - registeredTouchDevices << "pending handler(s)" ;
+ qCDebug(qLcEvdevTouch, "evdevtouch: Updating QInputDeviceManager device count: %d touch devices, %d pending handler(s)",
+ registeredTouchDevices, m_activeDevices.count() - registeredTouchDevices);
QInputDeviceManagerPrivate::get(QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount(
QInputDeviceManager::DeviceTypeTouch, registeredTouchDevices);
diff --git a/src/platformsupport/input/evdevtouch/qevdevtouchmanager_p.h b/src/platformsupport/input/evdevtouch/qevdevtouchmanager_p.h
index b9b772fb3a..94ee05d900 100644
--- a/src/platformsupport/input/evdevtouch/qevdevtouchmanager_p.h
+++ b/src/platformsupport/input/evdevtouch/qevdevtouchmanager_p.h
@@ -51,6 +51,8 @@
// We mean it.
//
+#include <QtInputSupport/private/devicehandlerlist_p.h>
+
#include <QObject>
#include <QHash>
#include <QSocketNotifier>
@@ -73,8 +75,7 @@ public:
private:
QString m_spec;
- QDeviceDiscovery *m_deviceDiscovery;
- QHash<QString, QEvdevTouchScreenHandlerThread *> m_activeDevices;
+ QtInputSupport::DeviceHandlerList<QEvdevTouchScreenHandlerThread> m_activeDevices;
};
QT_END_NAMESPACE
diff --git a/src/platformsupport/input/shared/devicehandlerlist_p.h b/src/platformsupport/input/shared/devicehandlerlist_p.h
new file mode 100644
index 0000000000..97794d4d7d
--- /dev/null
+++ b/src/platformsupport/input/shared/devicehandlerlist_p.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTINPUTSUPPORT_DEVICEHANDLERLIST_P_H
+#define QTINPUTSUPPORT_DEVICEHANDLERLIST_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QString>
+
+#include <vector>
+#include <memory>
+
+namespace QtInputSupport {
+
+template <typename Handler>
+class DeviceHandlerList {
+public:
+ struct Device {
+ QString deviceNode;
+ std::unique_ptr<Handler> handler;
+ };
+
+ void add(const QString &deviceNode, std::unique_ptr<Handler> handler)
+ {
+ v.push_back({deviceNode, std::move(handler)});
+ }
+
+ bool remove(const QString &deviceNode)
+ {
+ const auto deviceNodeMatches = [&] (const Device &d) { return d.deviceNode == deviceNode; };
+ const auto it = std::find_if(v.cbegin(), v.cend(), deviceNodeMatches);
+ if (it == v.cend())
+ return false;
+ v.erase(it);
+ return true;
+ }
+
+ int count() const noexcept { return static_cast<int>(v.size()); }
+
+ typename std::vector<Device>::const_iterator begin() const noexcept { return v.begin(); }
+ typename std::vector<Device>::const_iterator end() const noexcept { return v.end(); }
+
+private:
+ std::vector<Device> v;
+};
+
+} // QtInputSupport
+
+#endif // QTINPUTSUPPORT_DEVICEHANDLERLIST_P_H
diff --git a/src/platformsupport/input/shared/qevdevutil.cpp b/src/platformsupport/input/shared/qevdevutil.cpp
new file mode 100644
index 0000000000..74f8bcdc2b
--- /dev/null
+++ b/src/platformsupport/input/shared/qevdevutil.cpp
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qevdevutil_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QEvdevUtil {
+
+ParsedSpecification parseSpecification(const QString &specification)
+{
+ ParsedSpecification result;
+
+ result.args = specification.splitRef(QLatin1Char(':'));
+
+ for (const QStringRef &arg : qAsConst(result.args)) {
+ if (arg.startsWith(QLatin1String("/dev/"))) {
+ // if device is specified try to use it
+ result.devices.append(arg.toString());
+ } else {
+ // build new specification without /dev/ elements
+ result.spec += arg + QLatin1Char(':');
+ }
+ }
+
+ if (!result.spec.isEmpty())
+ result.spec.chop(1); // remove trailing ':'
+
+ return result;
+}
+
+} // namespace QEvdevUtil
+
+QT_END_NAMESPACE
diff --git a/src/platformsupport/input/shared/qevdevutil_p.h b/src/platformsupport/input/shared/qevdevutil_p.h
new file mode 100644
index 0000000000..7d0a5af130
--- /dev/null
+++ b/src/platformsupport/input/shared/qevdevutil_p.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QEVDEVUTIL_P_H
+#define QEVDEVUTIL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QString>
+#include <QStringList>
+#include <QVector>
+#include <QStringRef>
+
+QT_BEGIN_NAMESPACE
+
+namespace QEvdevUtil {
+
+struct ParsedSpecification
+{
+ QString spec;
+ QStringList devices;
+ QVector<QStringRef> args;
+};
+
+ParsedSpecification parseSpecification(const QString &specification);
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QEVDEVUTIL_P_H
diff --git a/src/platformsupport/input/shared/shared.pri b/src/platformsupport/input/shared/shared.pri
index 1443235244..c29d11e7d6 100644
--- a/src/platformsupport/input/shared/shared.pri
+++ b/src/platformsupport/input/shared/shared.pri
@@ -1,5 +1,8 @@
HEADERS += \
+ $$PWD/devicehandlerlist_p.h \
+ $$PWD/qevdevutil_p.h \
$$PWD/qtouchoutputmapping_p.h
SOURCES += \
+ $$PWD/qevdevutil.cpp \
$$PWD/qtouchoutputmapping.cpp
diff --git a/src/platformsupport/linuxaccessibility/atspiadaptor.cpp b/src/platformsupport/linuxaccessibility/atspiadaptor.cpp
index 580cf0e31d..8a825f8284 100644
--- a/src/platformsupport/linuxaccessibility/atspiadaptor.cpp
+++ b/src/platformsupport/linuxaccessibility/atspiadaptor.cpp
@@ -2050,8 +2050,8 @@ QVariantList AtSpiAdaptor::getAttributes(QAccessibleInterface *interface, int of
int endOffset;
QString joined = interface->textInterface()->attributes(offset, &startOffset, &endOffset);
- QStringList attributes = joined.split (QLatin1Char(';'), QString::SkipEmptyParts, Qt::CaseSensitive);
- foreach (const QString &attr, attributes) {
+ const QStringList attributes = joined.split (QLatin1Char(';'), QString::SkipEmptyParts, Qt::CaseSensitive);
+ for (const QString &attr : attributes) {
QStringList items;
items = attr.split(QLatin1Char(':'), QString::SkipEmptyParts, Qt::CaseSensitive);
AtSpiAttribute attribute = atspiTextAttribute(items[0], items[1]);
@@ -2069,14 +2069,13 @@ QVariantList AtSpiAdaptor::getAttributeValue(QAccessibleInterface *interface, in
{
QString mapped;
QString joined;
- QStringList attributes;
QSpiAttributeSet map;
int startOffset;
int endOffset;
joined = interface->textInterface()->attributes(offset, &startOffset, &endOffset);
- attributes = joined.split (QLatin1Char(';'), QString::SkipEmptyParts, Qt::CaseSensitive);
- foreach (const QString& attr, attributes) {
+ const QStringList attributes = joined.split (QLatin1Char(';'), QString::SkipEmptyParts, Qt::CaseSensitive);
+ for (const QString& attr : attributes) {
QStringList items;
items = attr.split(QLatin1Char(':'), QString::SkipEmptyParts, Qt::CaseSensitive);
AtSpiAttribute attribute = atspiTextAttribute(items[0], items[1]);
diff --git a/src/platformsupport/linuxaccessibility/bridge.cpp b/src/platformsupport/linuxaccessibility/bridge.cpp
index a96fe258df..fdc8cd3198 100644
--- a/src/platformsupport/linuxaccessibility/bridge.cpp
+++ b/src/platformsupport/linuxaccessibility/bridge.cpp
@@ -265,6 +265,8 @@ static RoleMapping map[] = {
//: Role of an accessible object
{ QAccessible::Desktop, ATSPI_ROLE_DESKTOP_FRAME, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "desktop") },
//: Role of an accessible object
+ { QAccessible::Notification, ATSPI_ROLE_NOTIFICATION, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "notification") },
+ //: Role of an accessible object
{ QAccessible::UserRole, ATSPI_ROLE_UNKNOWN, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "unknown") }
};
diff --git a/src/platformsupport/vkconvenience/qvkconvenience.cpp b/src/platformsupport/vkconvenience/qvkconvenience.cpp
index 462cdc9e0d..acde1d1bda 100644
--- a/src/platformsupport/vkconvenience/qvkconvenience.cpp
+++ b/src/platformsupport/vkconvenience/qvkconvenience.cpp
@@ -51,6 +51,7 @@ QT_BEGIN_NAMESPACE
\ingroup qpa
*/
+#if QT_CONFIG(opengl)
VkFormat QVkConvenience::vkFormatFromGlFormat(uint glFormat)
{
using GlFormat = QOpenGLTexture::TextureFormat;
@@ -211,5 +212,6 @@ VkFormat QVkConvenience::vkFormatFromGlFormat(uint glFormat)
default: return VK_FORMAT_UNDEFINED;
}
}
+#endif
QT_END_NAMESPACE
diff --git a/src/platformsupport/vkconvenience/qvkconvenience_p.h b/src/platformsupport/vkconvenience/qvkconvenience_p.h
index 1dd1dfc4a7..580271b593 100644
--- a/src/platformsupport/vkconvenience/qvkconvenience_p.h
+++ b/src/platformsupport/vkconvenience/qvkconvenience_p.h
@@ -59,7 +59,9 @@ QT_BEGIN_NAMESPACE
class QVkConvenience
{
public:
+#if QT_CONFIG(opengl)
static VkFormat vkFormatFromGlFormat(uint glFormat);
+#endif
};
QT_END_NAMESPACE
diff --git a/src/plugins/bearer/networkmanager/qnetworkmanagerservice.cpp b/src/plugins/bearer/networkmanager/qnetworkmanagerservice.cpp
index 3e77580015..35199eb7a2 100644
--- a/src/plugins/bearer/networkmanager/qnetworkmanagerservice.cpp
+++ b/src/plugins/bearer/networkmanager/qnetworkmanagerservice.cpp
@@ -72,11 +72,9 @@ QNetworkManagerInterface::QNetworkManagerInterface(QObject *parent)
QLatin1String(NM_DBUS_PATH),
DBUS_PROPERTIES_INTERFACE,
QDBusConnection::systemBus());
- QList<QVariant> argumentList;
- argumentList << QLatin1String(NM_DBUS_INTERFACE);
QDBusPendingReply<QVariantMap> propsReply
- = managerPropertiesInterface.callWithArgumentList(QDBus::Block,QLatin1String("GetAll"),
- argumentList);
+ = managerPropertiesInterface.call(QDBus::Block, QLatin1String("GetAll"), QLatin1String(NM_DBUS_INTERFACE));
+
if (!propsReply.isError()) {
propertyMap = propsReply.value();
} else {
@@ -344,11 +342,8 @@ QNetworkManagerInterfaceDevice::QNetworkManagerInterfaceDevice(const QString &de
DBUS_PROPERTIES_INTERFACE,
QDBusConnection::systemBus(),parent);
- QList<QVariant> argumentList;
- argumentList << QLatin1String(NM_DBUS_INTERFACE_DEVICE);
QDBusPendingReply<QVariantMap> propsReply
- = devicePropertiesInterface.callWithArgumentList(QDBus::Block,QLatin1String("GetAll"),
- argumentList);
+ = devicePropertiesInterface.call(QDBus::Block, QLatin1String("GetAll"), QLatin1String(NM_DBUS_INTERFACE_DEVICE));
if (!propsReply.isError()) {
propertyMap = propsReply.value();
@@ -446,11 +441,8 @@ QNetworkManagerInterfaceDeviceWired::QNetworkManagerInterfaceDeviceWired(const Q
DBUS_PROPERTIES_INTERFACE,
QDBusConnection::systemBus(),parent);
- QList<QVariant> argumentList;
- argumentList << QLatin1String(NM_DBUS_INTERFACE_DEVICE_WIRED);
QDBusPendingReply<QVariantMap> propsReply
- = deviceWiredPropertiesInterface.callWithArgumentList(QDBus::Block,QLatin1String("GetAll"),
- argumentList);
+ = deviceWiredPropertiesInterface.call(QDBus::Block, QLatin1String("GetAll"), QLatin1String(NM_DBUS_INTERFACE_DEVICE_WIRED));
if (!propsReply.isError()) {
propertyMap = propsReply.value();
@@ -543,11 +535,9 @@ QNetworkManagerInterfaceDeviceWireless::QNetworkManagerInterfaceDeviceWireless(c
DBUS_PROPERTIES_INTERFACE,
QDBusConnection::systemBus(),parent);
- QList<QVariant> argumentList;
- argumentList << QLatin1String(NM_DBUS_INTERFACE_DEVICE_WIRELESS);
QDBusPendingReply<QVariantMap> propsReply
- = deviceWirelessPropertiesInterface.callWithArgumentList(QDBus::Block,QLatin1String("GetAll"),
- argumentList);
+ = deviceWirelessPropertiesInterface.call(QDBus::Block, QLatin1String("GetAll"), QLatin1String(NM_DBUS_INTERFACE_DEVICE_WIRELESS));
+
if (!propsReply.isError()) {
propertyMap = propsReply.value();
}
@@ -647,11 +637,9 @@ QNetworkManagerInterfaceDeviceModem::QNetworkManagerInterfaceDeviceModem(const Q
QLatin1String("org.freedesktop.DBus.Properties"),
QDBusConnection::systemBus(),parent);
- QList<QVariant> argumentList;
- argumentList << QLatin1String(NM_DBUS_INTERFACE_DEVICE_MODEM);
QDBusPendingReply<QVariantMap> propsReply
- = deviceModemPropertiesInterface.callWithArgumentList(QDBus::Block,QLatin1String("GetAll"),
- argumentList);
+ = deviceModemPropertiesInterface.call(QDBus::Block, QLatin1String("GetAll"), QLatin1String(NM_DBUS_INTERFACE_DEVICE_MODEM));
+
if (!propsReply.isError()) {
propertyMap = propsReply.value();
}
@@ -746,9 +734,7 @@ QList <QDBusObjectPath> QNetworkManagerSettings::listConnections()
QString QNetworkManagerSettings::getConnectionByUuid(const QString &uuid)
{
- QList<QVariant> argumentList;
- argumentList << QVariant::fromValue(uuid);
- QDBusReply<QDBusObjectPath > reply = callWithArgumentList(QDBus::Block,QLatin1String("GetConnectionByUuid"), argumentList);
+ QDBusReply<QDBusObjectPath > reply = call(QDBus::Block, QLatin1String("GetConnectionByUuid"), uuid);
return reply.value().path();
}
@@ -917,11 +903,8 @@ QNetworkManagerConnectionActive::QNetworkManagerConnectionActive(const QString &
QDBusConnection::systemBus());
- QList<QVariant> argumentList;
- argumentList << QLatin1String(NM_DBUS_INTERFACE_ACTIVE_CONNECTION);
QDBusPendingReply<QVariantMap> propsReply
- = connectionActivePropertiesInterface.callWithArgumentList(QDBus::Block,QLatin1String("GetAll"),
- argumentList);
+ = connectionActivePropertiesInterface.call(QDBus::Block, QLatin1String("GetAll"), QLatin1String(NM_DBUS_INTERFACE_ACTIVE_CONNECTION));
if (!propsReply.isError()) {
propertyMap = propsReply.value();
diff --git a/src/plugins/bearer/qnetworksession_impl.cpp b/src/plugins/bearer/qnetworksession_impl.cpp
index a09ae72cb5..c6b678ab20 100644
--- a/src/plugins/bearer/qnetworksession_impl.cpp
+++ b/src/plugins/bearer/qnetworksession_impl.cpp
@@ -56,12 +56,13 @@ QT_BEGIN_NAMESPACE
static QBearerEngineImpl *getEngineFromId(const QString &id)
{
QNetworkConfigurationManagerPrivate *priv = qNetworkConfigurationManagerPrivate();
-
- const auto engines = priv->engines();
- for (QBearerEngine *engine : engines) {
- QBearerEngineImpl *engineImpl = qobject_cast<QBearerEngineImpl *>(engine);
- if (engineImpl && engineImpl->hasIdentifier(id))
- return engineImpl;
+ if (priv) {
+ const auto engines = priv->engines();
+ for (QBearerEngine *engine : engines) {
+ QBearerEngineImpl *engineImpl = qobject_cast<QBearerEngineImpl *>(engine);
+ if (engineImpl && engineImpl->hasIdentifier(id))
+ return engineImpl;
+ }
}
return 0;
diff --git a/src/plugins/platforms/android/qandroideventdispatcher.cpp b/src/plugins/platforms/android/qandroideventdispatcher.cpp
index e12551283f..3a1fb7a6de 100644
--- a/src/plugins/platforms/android/qandroideventdispatcher.cpp
+++ b/src/plugins/platforms/android/qandroideventdispatcher.cpp
@@ -78,14 +78,14 @@ void QAndroidEventDispatcher::stop()
void QAndroidEventDispatcher::goingToStop(bool stop)
{
- m_goingToStop.store(stop ? 1 : 0);
+ m_goingToStop.storeRelaxed(stop ? 1 : 0);
if (!stop)
wakeUp();
}
bool QAndroidEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
{
- if (m_goingToStop.load())
+ if (m_goingToStop.loadRelaxed())
flags |= QEventLoop::ExcludeSocketNotifiers | QEventLoop::X11ExcludeTimers;
{
diff --git a/src/plugins/platforms/android/qandroideventdispatcher.h b/src/plugins/platforms/android/qandroideventdispatcher.h
index e6f903bced..4fdd7af7a5 100644
--- a/src/plugins/platforms/android/qandroideventdispatcher.h
+++ b/src/plugins/platforms/android/qandroideventdispatcher.h
@@ -68,7 +68,7 @@ class QAndroidEventDispatcherStopper
{
public:
static QAndroidEventDispatcherStopper *instance();
- static bool stopped() {return !instance()->m_started.load(); }
+ static bool stopped() {return !instance()->m_started.loadRelaxed(); }
void startAll();
void stopAll();
void addEventDispatcher(QAndroidEventDispatcher *dispatcher);
diff --git a/src/plugins/platforms/android/qandroidinputcontext.cpp b/src/plugins/platforms/android/qandroidinputcontext.cpp
index db40c30d7d..fa07af8c46 100644
--- a/src/plugins/platforms/android/qandroidinputcontext.cpp
+++ b/src/plugins/platforms/android/qandroidinputcontext.cpp
@@ -64,29 +64,33 @@
QT_BEGIN_NAMESPACE
-template <typename T>
-class ScopedValueChangeBack
+namespace {
+
+class BatchEditLock
{
public:
- ScopedValueChangeBack(T &variable, T newValue)
- : m_oldValue(variable),
- m_variable(variable)
- {
- m_variable = newValue;
- }
- inline void setOldValue()
+
+ explicit BatchEditLock(QAndroidInputContext *context)
+ : m_context(context)
{
- m_variable = m_oldValue;
+ m_context->beginBatchEdit();
}
- ~ScopedValueChangeBack()
+
+ ~BatchEditLock()
{
- setOldValue();
+ m_context->endBatchEdit();
}
+
+ BatchEditLock(const BatchEditLock &) = delete;
+ BatchEditLock &operator=(const BatchEditLock &) = delete;
+
private:
- T m_oldValue;
- T &m_variable;
+
+ QAndroidInputContext *m_context;
};
+} // namespace anonymous
+
static QAndroidInputContext *m_androidInputContext = 0;
static char const *const QtNativeInputConnectionClassName = "org/qtproject/qt5/android/QtNativeInputConnection";
static char const *const QtExtractedTextClassName = "org/qtproject/qt5/android/QtExtractedText";
@@ -423,8 +427,12 @@ static QRect inputItemRectangle()
}
QAndroidInputContext::QAndroidInputContext()
- : QPlatformInputContext(), m_composingTextStart(-1), m_blockUpdateSelection(false),
- m_handleMode(Hidden), m_batchEditNestingLevel(0), m_focusObject(0)
+ : QPlatformInputContext()
+ , m_composingTextStart(-1)
+ , m_composingCursor(-1)
+ , m_handleMode(Hidden)
+ , m_batchEditNestingLevel(0)
+ , m_focusObject(0)
{
jclass clazz = QJNIEnvironmentPrivate::findClass(QtNativeInputConnectionClassName);
if (Q_UNLIKELY(!clazz)) {
@@ -565,13 +573,13 @@ void QAndroidInputContext::reset()
void QAndroidInputContext::commit()
{
- finishComposingText();
+ focusObjectStopComposing();
}
void QAndroidInputContext::updateCursorPosition()
{
QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
- if (!query.isNull() && !m_blockUpdateSelection && !m_batchEditNestingLevel) {
+ if (!query.isNull() && m_batchEditNestingLevel == 0) {
const int cursorPos = getAbsoluteCursorPosition(query);
const int composeLength = m_composingText.length();
@@ -579,24 +587,29 @@ void QAndroidInputContext::updateCursorPosition()
if (m_composingText.isEmpty() != (m_composingTextStart == -1))
qWarning() << "Input method out of sync" << m_composingText << m_composingTextStart;
- int realCursorPosition = cursorPos;
- int realAnchorPosition = cursorPos;
+ int realSelectionStart = cursorPos;
+ int realSelectionEnd = cursorPos;
int cpos = query->value(Qt::ImCursorPosition).toInt();
int anchor = query->value(Qt::ImAnchorPosition).toInt();
if (cpos != anchor) {
if (!m_composingText.isEmpty()) {
qWarning("Selecting text while preediting may give unpredictable results.");
- finishComposingText();
+ focusObjectStopComposing();
}
int blockPos = getBlockPosition(query);
- realCursorPosition = blockPos + cpos;
- realAnchorPosition = blockPos + anchor;
+ realSelectionStart = blockPos + cpos;
+ realSelectionEnd = blockPos + anchor;
}
// Qt's idea of the cursor position is the start of the preedit area, so we maintain our own preedit cursor pos
- if (!m_composingText.isEmpty())
- realCursorPosition = realAnchorPosition = m_composingCursor;
- QtAndroidInput::updateSelection(realCursorPosition, realAnchorPosition,
+ if (focusObjectIsComposing())
+ realSelectionStart = realSelectionEnd = m_composingCursor;
+
+ // Some keyboards misbahave when selStart > selEnd
+ if (realSelectionStart > realSelectionEnd)
+ std::swap(realSelectionStart, realSelectionEnd);
+
+ QtAndroidInput::updateSelection(realSelectionStart, realSelectionEnd,
m_composingTextStart, m_composingTextStart + composeLength); // pre-edit text
}
}
@@ -666,13 +679,11 @@ void QAndroidInputContext::updateSelectionHandles()
*/
void QAndroidInputContext::handleLocationChanged(int handleId, int x, int y)
{
- if (m_batchEditNestingLevel.load() || m_blockUpdateSelection) {
+ if (m_batchEditNestingLevel != 0) {
qWarning() << "QAndroidInputContext::handleLocationChanged returned";
return;
}
- finishComposingText();
-
auto im = qGuiApp->inputMethod();
auto leftRect = im->cursorRectangle();
// The handle is down of the cursor, but we want the position in the middle.
@@ -682,63 +693,96 @@ void QAndroidInputContext::handleLocationChanged(int handleId, int x, int y)
: QHighDpiScaling::factor(QtAndroid::androidPlatformIntegration()->screen());
QPointF point(x / pixelDensity, y / pixelDensity);
point.setY(point.y() - leftRect.width() / 2);
- if (handleId == 1) {
- setSelectionOnFocusObject(point, point);
- return;
- }
- QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImAnchorPosition | Qt::ImCurrentSelection);
+ QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImAnchorPosition
+ | Qt::ImAbsolutePosition | Qt::ImCurrentSelection);
QCoreApplication::sendEvent(m_focusObject, &query);
int cpos = query.value(Qt::ImCursorPosition).toInt();
int anchor = query.value(Qt::ImAnchorPosition).toInt();
- bool rtl = query.value(Qt::ImCurrentSelection).toString().isRightToLeft();
auto rightRect = im->anchorRectangle();
if (cpos > anchor)
std::swap(leftRect, rightRect);
- auto checkLeftHandle = [&rightRect](QPointF &handlePos) {
- if (handlePos.y() > rightRect.center().y())
- handlePos.setY(rightRect.center().y()); // adjust Y handle pos
- if (handlePos.y() >= rightRect.y() && handlePos.y() <= rightRect.bottom() && handlePos.x() >= rightRect.x())
- return false; // same line and wrong X pos ?
- return true;
- };
-
- auto checkRtlRightHandle = [&rightRect](QPointF &handlePos) {
- if (handlePos.y() > rightRect.center().y())
- handlePos.setY(rightRect.center().y()); // adjust Y handle pos
- if (handlePos.y() >= rightRect.y() && handlePos.y() <= rightRect.bottom() && rightRect.x() >= handlePos.x())
- return false; // same line and wrong X pos ?
- return true;
- };
-
- auto checkRightHandle = [&leftRect](QPointF &handlePos) {
- if (handlePos.y() < leftRect.center().y())
- handlePos.setY(leftRect.center().y()); // adjust Y handle pos
- if (handlePos.y() >= leftRect.y() && handlePos.y() <= leftRect.bottom() && leftRect.x() >= handlePos.x())
- return false; // same line and wrong X pos ?
- return true;
- };
-
- auto checkRtlLeftHandle = [&leftRect](QPointF &handlePos) {
- if (handlePos.y() < leftRect.center().y())
- handlePos.setY(leftRect.center().y()); // adjust Y handle pos
- if (handlePos.y() >= leftRect.y() && handlePos.y() <= leftRect.bottom() && handlePos.x() >= leftRect.x())
- return false; // same line and wrong X pos ?
- return true;
- };
-
- if (handleId == 2) {
- QPointF rightPoint(rightRect.center());
- if ((!rtl && !checkLeftHandle(point)) || (rtl && !checkRtlRightHandle(point)))
- return;
- setSelectionOnFocusObject(point, rightPoint);
+ // Do not allow dragging left handle below right handle, or right handle above left handle
+ if (handleId == 2 && point.y() > rightRect.center().y()) {
+ point.setY(rightRect.center().y());
+ } else if (handleId == 3 && point.y() < leftRect.center().y()) {
+ point.setY(leftRect.center().y());
+ }
+
+ const QPointF pointLocal = im->inputItemTransform().inverted().map(point);
+ bool ok;
+ const int handlePos =
+ QInputMethod::queryFocusObject(Qt::ImCursorPosition, pointLocal).toInt(&ok);
+ if (!ok)
+ return;
+
+ int newCpos = cpos;
+ int newAnchor = anchor;
+ if (newAnchor > newCpos)
+ std::swap(newAnchor, newCpos);
+
+ if (handleId == 1) {
+ newCpos = handlePos;
+ newAnchor = handlePos;
+ } else if (handleId == 2) {
+ newAnchor = handlePos;
} else if (handleId == 3) {
- QPointF leftPoint(leftRect.center());
- if ((!rtl && !checkRightHandle(point)) || (rtl && !checkRtlLeftHandle(point)))
+ newCpos = handlePos;
+ }
+
+ /*
+ Do not allow clearing selection by dragging selection handles and do not allow swapping
+ selection handles for consistency with Android's native text editing controls. Ensure that at
+ least one symbol remains selected.
+ */
+ if ((handleId == 2 || handleId == 3) && newCpos <= newAnchor) {
+ QTextBoundaryFinder finder(QTextBoundaryFinder::Grapheme,
+ query.value(Qt::ImCurrentSelection).toString());
+
+ const int oldSelectionStartPos = qMin(cpos, anchor);
+
+ if (handleId == 2) {
+ finder.toEnd();
+ finder.toPreviousBoundary();
+ newAnchor = finder.position() + oldSelectionStartPos;
+ } else {
+ finder.toStart();
+ finder.toNextBoundary();
+ newCpos = finder.position() + oldSelectionStartPos;
+ }
+ }
+
+ // Check if handle has been dragged far enough
+ if (!focusObjectIsComposing() && newCpos == cpos && newAnchor == anchor)
+ return;
+
+ /*
+ If the editor is currently in composing state, we have to compare newCpos with
+ m_composingCursor instead of cpos. And since there is nothing to compare with newAnchor, we
+ perform the check only when user drags the cursor handle.
+ */
+ if (focusObjectIsComposing() && handleId == 1) {
+ int absoluteCpos = query.value(Qt::ImAbsolutePosition).toInt(&ok);
+ if (!ok)
+ absoluteCpos = cpos;
+ const int blockPos = absoluteCpos - cpos;
+
+ if (blockPos + newCpos == m_composingCursor)
return;
- setSelectionOnFocusObject(leftPoint, point);
}
+
+ BatchEditLock batchEditLock(this);
+
+ focusObjectStopComposing();
+
+ QList<QInputMethodEvent::Attribute> attributes;
+ attributes.append({ QInputMethodEvent::Selection, newAnchor, newCpos - newAnchor });
+ if (newCpos != newAnchor)
+ attributes.append({ QInputMethodEvent::Cursor, 0, 0 });
+
+ QInputMethodEvent event(QString(), attributes);
+ QGuiApplication::sendEvent(m_focusObject, &event);
}
void QAndroidInputContext::touchDown(int x, int y)
@@ -748,7 +792,7 @@ void QAndroidInputContext::touchDown(int x, int y)
m_handleMode = ShowCursor;
// The VK will appear in a moment, stop the timer
m_hideCursorHandleTimer.stop();
- finishComposingText();
+ focusObjectStopComposing();
updateSelectionHandles();
}
}
@@ -760,13 +804,19 @@ void QAndroidInputContext::longPress(int x, int y)
return;
if (m_focusObject && inputItemRectangle().contains(x, y)) {
- finishComposingText();
+ BatchEditLock batchEditLock(this);
+
+ focusObjectStopComposing();
// Release left button, otherwise the following events will cancel the menu popup
QtAndroidInput::releaseMouse(x, y);
- handleLocationChanged(1, x, y);
- ScopedValueChangeBack<bool> svcb(m_blockUpdateSelection, true);
+ const double pixelDensity =
+ QGuiApplication::focusWindow()
+ ? QHighDpiScaling::factor(QGuiApplication::focusWindow())
+ : QHighDpiScaling::factor(QtAndroid::androidPlatformIntegration()->screen());
+ const QPointF touchPoint(x / pixelDensity, y / pixelDensity);
+ setSelectionOnFocusObject(touchPoint, touchPoint);
QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImAnchorPosition | Qt::ImTextBeforeCursor | Qt::ImTextAfterCursor);
QCoreApplication::sendEvent(m_focusObject, &query);
@@ -905,6 +955,7 @@ void QAndroidInputContext::clear()
{
m_composingText.clear();
m_composingTextStart = -1;
+ m_composingCursor = -1;
m_extractedText.clear();
}
@@ -912,9 +963,8 @@ void QAndroidInputContext::clear()
void QAndroidInputContext::setFocusObject(QObject *object)
{
if (object != m_focusObject) {
+ focusObjectStopComposing();
m_focusObject = object;
- if (!m_composingText.isEmpty())
- finishComposingText();
reset();
}
QPlatformInputContext::setFocusObject(object);
@@ -929,78 +979,135 @@ jboolean QAndroidInputContext::beginBatchEdit()
jboolean QAndroidInputContext::endBatchEdit()
{
- if (--m_batchEditNestingLevel == 0 && !m_blockUpdateSelection) //ending batch edit mode
+ if (--m_batchEditNestingLevel == 0) { //ending batch edit mode
+ focusObjectStartComposing();
updateCursorPosition();
+ }
return JNI_TRUE;
}
/*
- Android docs say: If composing, replace compose text with \a text.
- Otherwise insert \a text at current cursor position.
-
- The cursor should then be moved to newCursorPosition. If > 0, this is
- relative to the end of the text - 1; if <= 0, this is relative to the start
- of the text. updateSelection() needs to be called.
+ Android docs say: This behaves like calling setComposingText(text, newCursorPosition) then
+ finishComposingText().
*/
jboolean QAndroidInputContext::commitText(const QString &text, jint newCursorPosition)
{
- ScopedValueChangeBack<bool> svcb(m_blockUpdateSelection, true);
- QInputMethodEvent event;
- event.setCommitString(text);
- sendInputMethodEvent(&event);
- clear();
-
- // Qt has now put the cursor at the end of the text, corresponding to newCursorPosition == 1
- if (newCursorPosition != 1) {
- QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
- if (!query.isNull()) {
- QList<QInputMethodEvent::Attribute> attributes;
- const int localPos = query->value(Qt::ImCursorPosition).toInt();
- const int newLocalPos = newCursorPosition > 0
- ? localPos + newCursorPosition - 1
- : localPos - text.length() + newCursorPosition;
- //move the cursor
- attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection,
- newLocalPos, 0));
- }
- }
- svcb.setOldValue();
- updateCursorPosition();
- return JNI_TRUE;
+ BatchEditLock batchEditLock(this);
+ return setComposingText(text, newCursorPosition) && finishComposingText();
}
jboolean QAndroidInputContext::deleteSurroundingText(jint leftLength, jint rightLength)
{
+ BatchEditLock batchEditLock(this);
+
+ focusObjectStopComposing();
+
QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
if (query.isNull())
return JNI_TRUE;
- m_composingText.clear();
- m_composingTextStart = -1;
-
if (leftLength < 0) {
rightLength += -leftLength;
leftLength = 0;
}
+ const int initialBlockPos = getBlockPosition(query);
+ const int initialCursorPos = getAbsoluteCursorPosition(query);
+ const int initialAnchorPos = initialBlockPos + query->value(Qt::ImAnchorPosition).toInt();
+
+ /*
+ According to documentation, we should delete leftLength characters before current selection
+ and rightLength characters after current selection (without affecting selection). But that is
+ absolutely not what Android's native EditText does. It deletes leftLength characters before
+ min(selection start, composing region start) and rightLength characters after max(selection
+ end, composing region end). There are no known keyboards that depend on this behavior, but
+ it is better to be consistent with EditText behavior, because there definetly should be no
+ keyboards that depend on documented behavior.
+ */
+ const int leftEnd =
+ m_composingText.isEmpty()
+ ? qMin(initialCursorPos, initialAnchorPos)
+ : qMin(qMin(initialCursorPos, initialAnchorPos), m_composingTextStart);
+
+ const int rightBegin =
+ m_composingText.isEmpty()
+ ? qMax(initialCursorPos, initialAnchorPos)
+ : qMax(qMax(initialCursorPos, initialAnchorPos),
+ m_composingTextStart + m_composingText.length());
+
+ int textBeforeCursorLen;
+ int textAfterCursorLen;
+
QVariant textBeforeCursor = query->value(Qt::ImTextBeforeCursor);
QVariant textAfterCursor = query->value(Qt::ImTextAfterCursor);
if (textBeforeCursor.isValid() && textAfterCursor.isValid()) {
- leftLength = qMin(leftLength, textBeforeCursor.toString().length());
- rightLength = qMin(rightLength, textAfterCursor.toString().length());
+ textBeforeCursorLen = textBeforeCursor.toString().length();
+ textAfterCursorLen = textAfterCursor.toString().length();
} else {
- int cursorPos = query->value(Qt::ImCursorPosition).toInt();
- leftLength = qMin(leftLength, cursorPos);
- rightLength = qMin(rightLength, query->value(Qt::ImSurroundingText).toString().length() - cursorPos);
+ textBeforeCursorLen = initialCursorPos - initialBlockPos;
+ textAfterCursorLen =
+ query->value(Qt::ImSurroundingText).toString().length() - textBeforeCursorLen;
}
+ leftLength = qMin(qMax(0, textBeforeCursorLen - (initialCursorPos - leftEnd)), leftLength);
+ rightLength = qMin(qMax(0, textAfterCursorLen - (rightBegin - initialCursorPos)), rightLength);
+
if (leftLength == 0 && rightLength == 0)
return JNI_TRUE;
- QInputMethodEvent event;
- event.setCommitString(QString(), -leftLength, leftLength+rightLength);
- sendInputMethodEvent(&event);
- clear();
+ if (leftEnd == rightBegin) {
+ // We have no selection and no composing region; we can do everything using one event
+ QInputMethodEvent event;
+ event.setCommitString({}, -leftLength, leftLength + rightLength);
+ QGuiApplication::sendEvent(m_focusObject, &event);
+ } else {
+ if (initialCursorPos != initialAnchorPos) {
+ QInputMethodEvent event({}, {
+ { QInputMethodEvent::Selection, initialCursorPos - initialBlockPos, 0 }
+ });
+
+ QGuiApplication::sendEvent(m_focusObject, &event);
+ }
+
+ int currentCursorPos = initialCursorPos;
+
+ if (rightLength > 0) {
+ QInputMethodEvent event;
+ event.setCommitString({}, rightBegin - currentCursorPos, rightLength);
+ QGuiApplication::sendEvent(m_focusObject, &event);
+
+ currentCursorPos = rightBegin;
+ }
+
+ if (leftLength > 0) {
+ const int leftBegin = leftEnd - leftLength;
+
+ QInputMethodEvent event;
+ event.setCommitString({}, leftBegin - currentCursorPos, leftLength);
+ QGuiApplication::sendEvent(m_focusObject, &event);
+
+ currentCursorPos = leftBegin;
+
+ if (!m_composingText.isEmpty())
+ m_composingTextStart -= leftLength;
+ }
+
+ // Restore cursor position or selection
+ if (currentCursorPos != initialCursorPos - leftLength
+ || initialCursorPos != initialAnchorPos) {
+ // If we have deleted a newline character, we are now in a new block
+ const int currentBlockPos = getBlockPosition(
+ focusObjectInputMethodQuery(Qt::ImAbsolutePosition | Qt::ImCursorPosition));
+
+ QInputMethodEvent event({}, {
+ { QInputMethodEvent::Selection, initialCursorPos - leftLength - currentBlockPos,
+ initialAnchorPos - initialCursorPos },
+ { QInputMethodEvent::Cursor, 0, 0 }
+ });
+
+ QGuiApplication::sendEvent(m_focusObject, &event);
+ }
+ }
return JNI_TRUE;
}
@@ -1008,16 +1115,70 @@ jboolean QAndroidInputContext::deleteSurroundingText(jint leftLength, jint right
// Android docs say the cursor must not move
jboolean QAndroidInputContext::finishComposingText()
{
- if (m_composingText.isEmpty())
- return JNI_TRUE; // not composing
+ BatchEditLock batchEditLock(this);
+
+ if (!focusObjectStopComposing())
+ return JNI_FALSE;
+
+ clear();
+ return JNI_TRUE;
+}
+
+bool QAndroidInputContext::focusObjectIsComposing() const
+{
+ return m_composingCursor != -1;
+}
+
+void QAndroidInputContext::focusObjectStartComposing()
+{
+ if (focusObjectIsComposing() || m_composingText.isEmpty())
+ return;
+
+ // Composing strings containing newline characters are rare and may cause problems
+ if (m_composingText.contains(QLatin1Char('\n')))
+ return;
+
+ QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
+ if (!query)
+ return;
+
+ if (query->value(Qt::ImCursorPosition).toInt() != query->value(Qt::ImAnchorPosition).toInt())
+ return;
+
+ const int absoluteCursorPos = getAbsoluteCursorPosition(query);
+ if (absoluteCursorPos < m_composingTextStart
+ || absoluteCursorPos > m_composingTextStart + m_composingText.length())
+ return;
+
+ m_composingCursor = absoluteCursorPos;
+
+ QTextCharFormat underlined;
+ underlined.setFontUnderline(true);
+
+ QInputMethodEvent event(m_composingText, {
+ { QInputMethodEvent::Cursor, absoluteCursorPos - m_composingTextStart, 1 },
+ { QInputMethodEvent::TextFormat, 0, m_composingText.length(), underlined }
+ });
+
+ event.setCommitString({}, m_composingTextStart - absoluteCursorPos, m_composingText.length());
+
+ QGuiApplication::sendEvent(m_focusObject, &event);
+}
+
+bool QAndroidInputContext::focusObjectStopComposing()
+{
+ if (!focusObjectIsComposing())
+ return true; // not composing
QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
if (query.isNull())
- return JNI_FALSE;
+ return false;
const int blockPos = getBlockPosition(query);
const int localCursorPos = m_composingCursor - blockPos;
+ m_composingCursor = -1;
+
// Moving Qt's cursor to where the preedit cursor used to be
QList<QInputMethodEvent::Attribute> attributes;
attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, localCursorPos, 0));
@@ -1025,9 +1186,8 @@ jboolean QAndroidInputContext::finishComposingText()
QInputMethodEvent event(QString(), attributes);
event.setCommitString(m_composingText);
sendInputMethodEvent(&event);
- clear();
- return JNI_TRUE;
+ return true;
}
jint QAndroidInputContext::getCursorCapsMode(jint /*reqModes*/)
@@ -1067,52 +1227,51 @@ const QAndroidInputContext::ExtractedText &QAndroidInputContext::getExtractedTex
// updateExtractedText(View, int, ExtractedText) whenever you call
// updateSelection(View, int, int, int, int)." QTBUG-37980
- QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
+ QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(
+ Qt::ImCursorPosition | Qt::ImAbsolutePosition | Qt::ImAnchorPosition);
if (query.isNull())
return m_extractedText;
- int localPos = query->value(Qt::ImCursorPosition).toInt(); //position before pre-edit text relative to the current block
- int blockPos = getBlockPosition(query);
- QString blockText = query->value(Qt::ImSurroundingText).toString();
- int composeLength = m_composingText.length();
-
- if (composeLength > 0) {
- //Qt doesn't give us the preedit text, so we have to insert it at the correct position
- int localComposePos = m_composingTextStart - blockPos;
- blockText = blockText.leftRef(localComposePos) + m_composingText + blockText.midRef(localComposePos);
- }
-
- int cpos = localPos + composeLength; //actual cursor pos relative to the current block
-
- int localOffset = 0; // start of extracted text relative to the current block
- if (blockPos > 0) {
- QString prevBlockEnding = query->value(Qt::ImTextBeforeCursor).toString();
- prevBlockEnding.chop(localPos);
- if (prevBlockEnding.endsWith(QLatin1Char('\n'))) {
- localOffset = -qMin(20, prevBlockEnding.length());
- blockText = prevBlockEnding.right(-localOffset) + blockText;
- }
- }
+ const int cursorPos = getAbsoluteCursorPosition(query);
+ const int blockPos = getBlockPosition(query);
// It is documented that we should try to return hintMaxChars
- // characters, but that's not what the standard Android controls do, and
+ // characters, but standard Android controls always return all text, and
// there are input methods out there that (surprise) seem to depend on
// what happens in reality rather than what's documented.
- m_extractedText.text = blockText;
- m_extractedText.startOffset = blockPos + localOffset;
+ QVariant textBeforeCursor = QInputMethod::queryFocusObject(Qt::ImTextBeforeCursor, INT_MAX);
+ QVariant textAfterCursor = QInputMethod::queryFocusObject(Qt::ImTextAfterCursor, INT_MAX);
+ if (textBeforeCursor.isValid() && textAfterCursor.isValid()) {
+ if (focusObjectIsComposing()) {
+ m_extractedText.text =
+ textBeforeCursor.toString() + m_composingText + textAfterCursor.toString();
+ } else {
+ m_extractedText.text = textBeforeCursor.toString() + textAfterCursor.toString();
+ }
+
+ m_extractedText.startOffset = qMax(0, cursorPos - textBeforeCursor.toString().length());
+ } else {
+ m_extractedText.text = focusObjectInputMethodQuery(Qt::ImSurroundingText)
+ ->value(Qt::ImSurroundingText).toString();
+
+ if (focusObjectIsComposing())
+ m_extractedText.text.insert(cursorPos - blockPos, m_composingText);
- const QString &selection = query->value(Qt::ImCurrentSelection).toString();
- const int selLen = selection.length();
- if (selLen) {
- m_extractedText.selectionStart = query->value(Qt::ImAnchorPosition).toInt() - localOffset;
- m_extractedText.selectionEnd = m_extractedText.selectionStart + selLen;
- } else if (composeLength > 0) {
+ m_extractedText.startOffset = blockPos;
+ }
+
+ if (focusObjectIsComposing()) {
m_extractedText.selectionStart = m_composingCursor - m_extractedText.startOffset;
- m_extractedText.selectionEnd = m_composingCursor - m_extractedText.startOffset;
- } else {
- m_extractedText.selectionStart = cpos - localOffset;
- m_extractedText.selectionEnd = cpos - localOffset;
+ m_extractedText.selectionEnd = m_extractedText.selectionStart;
+ } else {
+ m_extractedText.selectionStart = cursorPos - m_extractedText.startOffset;
+ m_extractedText.selectionEnd =
+ blockPos + query->value(Qt::ImAnchorPosition).toInt() - m_extractedText.startOffset;
+
+ // Some keyboards misbehave when selectionStart > selectionEnd
+ if (m_extractedText.selectionStart > m_extractedText.selectionEnd)
+ std::swap(m_extractedText.selectionStart, m_extractedText.selectionEnd);
}
return m_extractedText;
@@ -1147,10 +1306,20 @@ QString QAndroidInputContext::getTextAfterCursor(jint length, jint /*flags*/)
}
}
- // Controls do not report preedit text, so we have to add it
- if (!m_composingText.isEmpty()) {
+ if (focusObjectIsComposing()) {
+ // Controls do not report preedit text, so we have to add it
const int cursorPosInsidePreedit = m_composingCursor - m_composingTextStart;
text = m_composingText.midRef(cursorPosInsidePreedit) + text;
+ } else {
+ // We must not return selected text if there is any
+ QSharedPointer<QInputMethodQueryEvent> query =
+ focusObjectInputMethodQuery(Qt::ImCursorPosition | Qt::ImAnchorPosition);
+ if (query) {
+ const int cursorPos = query->value(Qt::ImCursorPosition).toInt();
+ const int anchorPos = query->value(Qt::ImAnchorPosition).toInt();
+ if (anchorPos > cursorPos)
+ text.remove(0, anchorPos - cursorPos);
+ }
}
text.truncate(length);
@@ -1177,10 +1346,20 @@ QString QAndroidInputContext::getTextBeforeCursor(jint length, jint /*flags*/)
}
}
- // Controls do not report preedit text, so we have to add it
- if (!m_composingText.isEmpty()) {
+ if (focusObjectIsComposing()) {
+ // Controls do not report preedit text, so we have to add it
const int cursorPosInsidePreedit = m_composingCursor - m_composingTextStart;
text += m_composingText.leftRef(cursorPosInsidePreedit);
+ } else {
+ // We must not return selected text if there is any
+ QSharedPointer<QInputMethodQueryEvent> query =
+ focusObjectInputMethodQuery(Qt::ImCursorPosition | Qt::ImAnchorPosition);
+ if (query) {
+ const int cursorPos = query->value(Qt::ImCursorPosition).toInt();
+ const int anchorPos = query->value(Qt::ImAnchorPosition).toInt();
+ if (anchorPos < cursorPos)
+ text.chop(cursorPos - anchorPos);
+ }
}
if (text.length() > length)
@@ -1189,11 +1368,13 @@ QString QAndroidInputContext::getTextBeforeCursor(jint length, jint /*flags*/)
}
/*
- Android docs say that this function should remove the current preedit text
- if any, and replace it with the given text. Any selected text should be
- removed. The cursor is then moved to newCursorPosition. If > 0, this is
- relative to the end of the text - 1; if <= 0, this is relative to the start
- of the text.
+ Android docs say that this function should:
+ - remove the current composing text, if there is any
+ - otherwise remove currently selected text, if there is any
+ - insert new text in place of old composing text or, if there was none, at current cursor position
+ - mark the inserted text as composing
+ - move cursor as specified by newCursorPosition: if > 0, it is relative to the end of inserted
+ text - 1; if <= 0, it is relative to the start of inserted text
*/
jboolean QAndroidInputContext::setComposingText(const QString &text, jint newCursorPosition)
@@ -1202,47 +1383,110 @@ jboolean QAndroidInputContext::setComposingText(const QString &text, jint newCur
if (query.isNull())
return JNI_FALSE;
- const int cursorPos = getAbsoluteCursorPosition(query);
- if (newCursorPosition > 0)
- newCursorPosition += text.length() - 1;
+ BatchEditLock batchEditLock(this);
+
+ const int absoluteCursorPos = getAbsoluteCursorPosition(query);
+ int absoluteAnchorPos = getBlockPosition(query) + query->value(Qt::ImAnchorPosition).toInt();
+
+ // If we have composing region and selection (and therefore focusObjectIsComposing() == false),
+ // we must clear selection so that we won't delete it when we will be replacing composing text
+ if (!m_composingText.isEmpty() && absoluteCursorPos != absoluteAnchorPos) {
+ const int cursorPos = query->value(Qt::ImCursorPosition).toInt();
+ QInputMethodEvent event({}, { { QInputMethodEvent::Selection, cursorPos, 0 } });
+ QGuiApplication::sendEvent(m_focusObject, &event);
+
+ absoluteAnchorPos = absoluteCursorPos;
+ }
+
+ // If we had no composing region, pretend that we had a zero-length composing region at current
+ // cursor position to simplify code. Also account for that we must delete selected text if there
+ // (still) is any.
+ const int effectiveAbsoluteCursorPos = qMin(absoluteCursorPos, absoluteAnchorPos);
+ if (m_composingTextStart == -1)
+ m_composingTextStart = effectiveAbsoluteCursorPos;
+ const int oldComposingTextLen = m_composingText.length();
m_composingText = text;
- m_composingTextStart = text.isEmpty() ? -1 : cursorPos;
- m_composingCursor = cursorPos + newCursorPosition;
- QList<QInputMethodEvent::Attribute> attributes;
- attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor,
- newCursorPosition,
- 1));
- // Show compose text underlined
- QTextCharFormat underlined;
- underlined.setFontUnderline(true);
- attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,0, text.length(),
- QVariant(underlined)));
- QInputMethodEvent event(m_composingText, attributes);
- sendInputMethodEvent(&event);
+ const int newAbsoluteCursorPos =
+ newCursorPosition <= 0
+ ? m_composingTextStart + newCursorPosition
+ : m_composingTextStart + m_composingText.length() + newCursorPosition - 1;
- QMetaObject::invokeMethod(this, "keyDown");
+ const bool focusObjectWasComposing = focusObjectIsComposing();
+
+ // Same checks as in focusObjectStartComposing()
+ if (!m_composingText.isEmpty() && !m_composingText.contains(QLatin1Char('\n'))
+ && newAbsoluteCursorPos >= m_composingTextStart
+ && newAbsoluteCursorPos <= m_composingTextStart + m_composingText.length())
+ m_composingCursor = newAbsoluteCursorPos;
+ else
+ m_composingCursor = -1;
- updateCursorPosition();
+ QInputMethodEvent event;
+ if (focusObjectIsComposing()) {
+ QTextCharFormat underlined;
+ underlined.setFontUnderline(true);
+
+ event = QInputMethodEvent(m_composingText, {
+ { QInputMethodEvent::TextFormat, 0, m_composingText.length(), underlined },
+ { QInputMethodEvent::Cursor, m_composingCursor - m_composingTextStart, 1 }
+ });
+
+ if (oldComposingTextLen > 0 && !focusObjectWasComposing) {
+ event.setCommitString({}, m_composingTextStart - effectiveAbsoluteCursorPos,
+ oldComposingTextLen);
+ }
+ } else {
+ event = QInputMethodEvent({}, {});
+
+ if (focusObjectWasComposing) {
+ event.setCommitString(m_composingText);
+ } else {
+ event.setCommitString(m_composingText,
+ m_composingTextStart - effectiveAbsoluteCursorPos,
+ oldComposingTextLen);
+ }
+ }
+
+ if (m_composingText.isEmpty())
+ clear();
+
+ QGuiApplication::sendEvent(m_focusObject, &event);
+
+ if (!focusObjectIsComposing() && newCursorPosition != 1) {
+ // Move cursor using a separate event because if we have inserted or deleted a newline
+ // character, then we are now inside an another block
+
+ const int newBlockPos = getBlockPosition(
+ focusObjectInputMethodQuery(Qt::ImCursorPosition | Qt::ImAbsolutePosition));
+
+ event = QInputMethodEvent({}, {
+ { QInputMethodEvent::Selection, newAbsoluteCursorPos - newBlockPos, 0 }
+ });
+
+ QGuiApplication::sendEvent(m_focusObject, &event);
+ }
+
+ keyDown();
return JNI_TRUE;
}
// Android docs say:
// * start may be after end, same meaning as if swapped
-// * this function should not trigger updateSelection
+// * this function should not trigger updateSelection, but Android's native EditText does trigger it
// * if start == end then we should stop composing
jboolean QAndroidInputContext::setComposingRegion(jint start, jint end)
{
+ BatchEditLock batchEditLock(this);
+
// Qt will not include the current preedit text in the query results, and interprets all
// parameters relative to the text excluding the preedit. The simplest solution is therefore to
// tell Qt that we commit the text before we set the new region. This may cause a little flicker, but is
// much more robust than trying to keep the two different world views in sync
- bool wasComposing = !m_composingText.isEmpty();
- if (wasComposing)
- finishComposingText();
+ finishComposingText();
QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
if (query.isNull())
@@ -1253,54 +1497,42 @@ jboolean QAndroidInputContext::setComposingRegion(jint start, jint end)
if (start > end)
qSwap(start, end);
- /*
- start and end are cursor positions, not character positions,
- i.e. selecting the first character is done by start == 0 and end == 1,
- and start == end means no character selected
-
- Therefore, the length of the region is end - start
- */
-
- int length = end - start;
- int localPos = query->value(Qt::ImCursorPosition).toInt();
- int blockPosition = getBlockPosition(query);
- int localStart = start - blockPosition; // Qt uses position inside block
- int currentCursor = wasComposing ? m_composingCursor : blockPosition + localPos;
-
- ScopedValueChangeBack<bool> svcb(m_blockUpdateSelection, true);
-
QString text = query->value(Qt::ImSurroundingText).toString();
+ int textOffset = getBlockPosition(query);
- m_composingText = text.mid(localStart, length);
- m_composingTextStart = start;
- m_composingCursor = currentCursor;
-
- //in the Qt text controls, the preedit is defined relative to the cursor position
- int relativeStart = localStart - localPos;
+ if (start < textOffset || end > textOffset + text.length()) {
+ const int cursorPos = query->value(Qt::ImCursorPosition).toInt();
- QList<QInputMethodEvent::Attribute> attributes;
+ if (end - textOffset > text.length()) {
+ const QString after = query->value(Qt::ImTextAfterCursor).toString();
+ const int additionalSuffixLen = after.length() - (text.length() - cursorPos);
- // Show compose text underlined
- QTextCharFormat underlined;
- underlined.setFontUnderline(true);
- attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,0, length,
- QVariant(underlined)));
+ if (additionalSuffixLen > 0)
+ text += after.rightRef(additionalSuffixLen);
+ }
- // Keep the cursor position unchanged (don't move to end of preedit)
- attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, currentCursor - start, 1));
+ if (start < textOffset) {
+ QString before = query->value(Qt::ImTextBeforeCursor).toString();
+ before.chop(cursorPos);
- QInputMethodEvent event(m_composingText, attributes);
- event.setCommitString(QString(), relativeStart, length);
- sendInputMethodEvent(&event);
+ if (!before.isEmpty()) {
+ text = before + text;
+ textOffset -= before.length();
+ }
+ }
+ if (start < textOffset || end - textOffset > text.length()) {
#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- QSharedPointer<QInputMethodQueryEvent> query2 = focusObjectInputMethodQuery();
- if (!query2.isNull()) {
- qDebug() << "Setting. Prev local cpos:" << localPos << "block pos:" <<blockPosition << "comp.start:" << m_composingTextStart << "rel.start:" << relativeStart << "len:" << length << "cpos attr:" << localPos - localStart;
- qDebug() << "New cursor pos" << getAbsoluteCursorPosition(query2);
- }
+ qWarning("setComposingRegion: failed to retrieve text from composing region");
#endif
+ return JNI_TRUE;
+ }
+ }
+
+ m_composingText = text.mid(start - textOffset, end - start);
+ m_composingTextStart = start;
+
return JNI_TRUE;
}
@@ -1310,15 +1542,18 @@ jboolean QAndroidInputContext::setSelection(jint start, jint end)
if (query.isNull())
return JNI_FALSE;
+ BatchEditLock batchEditLock(this);
+
int blockPosition = getBlockPosition(query);
int localCursorPos = start - blockPosition;
- QList<QInputMethodEvent::Attribute> attributes;
- if (!m_composingText.isEmpty() && start == end) {
+ if (focusObjectIsComposing() && start == end && start >= m_composingTextStart
+ && start <= m_composingTextStart + m_composingText.length()) {
// not actually changing the selection; just moving the
// preedit cursor
int localOldPos = query->value(Qt::ImCursorPosition).toInt();
int pos = localCursorPos - localOldPos;
+ QList<QInputMethodEvent::Attribute> attributes;
attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, pos, 1));
//but we have to tell Qt about the compose text all over again
@@ -1330,21 +1565,26 @@ jboolean QAndroidInputContext::setSelection(jint start, jint end)
QVariant(underlined)));
m_composingCursor = start;
+ QInputMethodEvent event(m_composingText, attributes);
+ QGuiApplication::sendEvent(m_focusObject, &event);
} else {
// actually changing the selection
+ focusObjectStopComposing();
+ QList<QInputMethodEvent::Attribute> attributes;
attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection,
localCursorPos,
end - start));
+ QInputMethodEvent event({}, attributes);
+ QGuiApplication::sendEvent(m_focusObject, &event);
}
- QInputMethodEvent event(m_composingText, attributes);
- sendInputMethodEvent(&event);
- updateCursorPosition();
return JNI_TRUE;
}
jboolean QAndroidInputContext::selectAll()
{
- finishComposingText();
+ BatchEditLock batchEditLock(this);
+
+ focusObjectStopComposing();
m_handleMode = ShowCursor;
sendShortcut(QKeySequence::SelectAll);
return JNI_TRUE;
@@ -1352,7 +1592,12 @@ jboolean QAndroidInputContext::selectAll()
jboolean QAndroidInputContext::cut()
{
+ BatchEditLock batchEditLock(this);
+
+ // This is probably not what native EditText would do, but normally if there is selection, then
+ // there will be no composing region
finishComposingText();
+
m_handleMode = ShowCursor;
sendShortcut(QKeySequence::Cut);
return JNI_TRUE;
@@ -1360,7 +1605,9 @@ jboolean QAndroidInputContext::cut()
jboolean QAndroidInputContext::copy()
{
- finishComposingText();
+ BatchEditLock batchEditLock(this);
+
+ focusObjectStopComposing();
m_handleMode = ShowCursor;
sendShortcut(QKeySequence::Copy);
return JNI_TRUE;
@@ -1374,7 +1621,11 @@ jboolean QAndroidInputContext::copyURL()
jboolean QAndroidInputContext::paste()
{
+ BatchEditLock batchEditLock(this);
+
+ // TODO: This is not what native EditText does
finishComposingText();
+
m_handleMode = ShowCursor;
sendShortcut(QKeySequence::Paste);
return JNI_TRUE;
@@ -1386,8 +1637,12 @@ void QAndroidInputContext::sendShortcut(const QKeySequence &sequence)
const int keys = sequence[i];
Qt::Key key = Qt::Key(keys & ~Qt::KeyboardModifierMask);
Qt::KeyboardModifiers mod = Qt::KeyboardModifiers(keys & Qt::KeyboardModifierMask);
- QGuiApplication::postEvent(m_focusObject, new QKeyEvent(QEvent::KeyPress, key, mod));
- QGuiApplication::postEvent(m_focusObject, new QKeyEvent(QEvent::KeyRelease, key, mod));
+
+ QKeyEvent pressEvent(QEvent::KeyPress, key, mod);
+ QKeyEvent releaseEvent(QEvent::KeyRelease, key, mod);
+
+ QGuiApplication::sendEvent(m_focusObject, &pressEvent);
+ QGuiApplication::sendEvent(m_focusObject, &releaseEvent);
}
}
diff --git a/src/plugins/platforms/android/qandroidinputcontext.h b/src/plugins/platforms/android/qandroidinputcontext.h
index bd3edb30f0..e9bfb98e66 100644
--- a/src/plugins/platforms/android/qandroidinputcontext.h
+++ b/src/plugins/platforms/android/qandroidinputcontext.h
@@ -151,6 +151,9 @@ private slots:
private:
void sendInputMethodEvent(QInputMethodEvent *event);
QSharedPointer<QInputMethodQueryEvent> focusObjectInputMethodQuery(Qt::InputMethodQueries queries = Qt::ImQueryAll);
+ bool focusObjectIsComposing() const;
+ void focusObjectStartComposing();
+ bool focusObjectStopComposing();
private:
ExtractedText m_extractedText;
@@ -158,9 +161,8 @@ private:
int m_composingTextStart;
int m_composingCursor;
QMetaObject::Connection m_updateCursorPosConnection;
- bool m_blockUpdateSelection;
HandleModes m_handleMode;
- QAtomicInt m_batchEditNestingLevel;
+ int m_batchEditNestingLevel;
QObject *m_focusObject;
QTimer m_hideCursorHandleTimer;
};
diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.h b/src/plugins/platforms/cocoa/qcocoabackingstore.h
index acddc3ecc8..2398e6351e 100644
--- a/src/plugins/platforms/cocoa/qcocoabackingstore.h
+++ b/src/plugins/platforms/cocoa/qcocoabackingstore.h
@@ -55,6 +55,7 @@ public:
QNSWindowBackingStore(QWindow *window);
~QNSWindowBackingStore();
+ void resize(const QSize &size, const QRegion &staticContents) override;
void flush(QWindow *, const QRegion &, const QPoint &) override;
private:
diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
index c381f87844..01b4894324 100644
--- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm
+++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
@@ -71,6 +71,24 @@ QImage::Format QNSWindowBackingStore::format() const
return QRasterBackingStore::format();
}
+void QNSWindowBackingStore::resize(const QSize &size, const QRegion &staticContents)
+{
+ qCDebug(lcQpaBackingStore) << "Resize requested to" << size;
+ QRasterBackingStore::resize(size, staticContents);
+
+ // The window shadow rendered by AppKit is based on the shape/content of the
+ // NSWindow surface. Technically any flush of the backingstore can result in
+ // a potentially new shape of the window, and would need a shadow invalidation,
+ // but this is likely too expensive to do at every flush for the few cases where
+ // clients change the shape dynamically. One case where we do know that the shadow
+ // likely needs invalidation, if the window has partially transparent content,
+ // is after a resize, where AppKit's default shadow may be based on the previous
+ // window content.
+ QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window()->handle());
+ if (cocoaWindow->isContentView() && !cocoaWindow->isOpaque())
+ cocoaWindow->m_needsInvalidateShadow = true;
+}
+
/*!
Flushes the given \a region from the specified \a window onto the
screen.
@@ -217,6 +235,7 @@ void QNSWindowBackingStore::flush(QWindow *window, const QRegion &region, const
QCocoaWindow *topLevelCocoaWindow = static_cast<QCocoaWindow *>(topLevelWindow->handle());
if (Q_UNLIKELY(topLevelCocoaWindow->m_needsInvalidateShadow)) {
+ qCDebug(lcQpaBackingStore) << "Invalidating window shadow for" << topLevelCocoaWindow;
[topLevelView.window invalidateShadow];
topLevelCocoaWindow->m_needsInvalidateShadow = false;
}
@@ -382,10 +401,11 @@ void QCALayerBackingStore::ensureBackBuffer()
bool QCALayerBackingStore::recreateBackBufferIfNeeded()
{
- const qreal devicePixelRatio = window()->devicePixelRatio();
+ const QCocoaWindow *platformWindow = static_cast<QCocoaWindow *>(window()->handle());
+ const qreal devicePixelRatio = platformWindow->devicePixelRatio();
QSize requestedBufferSize = m_requestedSize * devicePixelRatio;
- const NSView *backingStoreView = static_cast<QCocoaWindow *>(window()->handle())->view();
+ const NSView *backingStoreView = platformWindow->view();
Q_UNUSED(backingStoreView);
auto bufferSizeMismatch = [&](const QSize requested, const QSize actual) {
diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h
index 9771cd0289..69587a24be 100644
--- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h
+++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h
@@ -191,6 +191,7 @@ public:
static void waitingObserverCallback(CFRunLoopObserverRef observer,
CFRunLoopActivity activity, void *info);
static void firstLoopEntry(CFRunLoopObserverRef ref, CFRunLoopActivity activity, void *info);
+ bool sendQueuedUserInputEvents();
void processPostedEvents();
};
diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
index 84ffadea83..d3bb0711f0 100644
--- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
+++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
@@ -377,16 +377,9 @@ bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
NSEvent* event = nil;
// First, send all previously excluded input events, if any:
- if (!excludeUserEvents) {
- while (!d->queuedUserInputEvents.isEmpty()) {
- event = static_cast<NSEvent *>(d->queuedUserInputEvents.takeFirst());
- if (!filterNativeEvent("NSEvent", event, nullptr)) {
- [NSApp sendEvent:event];
- retVal = true;
- }
- [event release];
- }
- }
+ if (d->sendQueuedUserInputEvents())
+ retVal = true;
+
// If Qt is used as a plugin, or as an extension in a native cocoa
// application, we should not run or stop NSApplication; This will be
@@ -843,6 +836,23 @@ void QCocoaEventDispatcherPrivate::waitingObserverCallback(CFRunLoopObserverRef,
emit static_cast<QCocoaEventDispatcher*>(info)->awake();
}
+bool QCocoaEventDispatcherPrivate::sendQueuedUserInputEvents()
+{
+ Q_Q(QCocoaEventDispatcher);
+ if (processEventsFlags & QEventLoop::ExcludeUserInputEvents)
+ return false;
+ bool didSendEvent = false;
+ while (!queuedUserInputEvents.isEmpty()) {
+ NSEvent *event = static_cast<NSEvent *>(queuedUserInputEvents.takeFirst());
+ if (!q->filterNativeEvent("NSEvent", event, nullptr)) {
+ [NSApp sendEvent:event];
+ didSendEvent = true;
+ }
+ [event release];
+ }
+ return didSendEvent;
+}
+
void QCocoaEventDispatcherPrivate::processPostedEvents()
{
if (blockSendPostedEvents) {
@@ -896,6 +906,7 @@ void QCocoaEventDispatcherPrivate::postedEventsSourceCallback(void *info)
d->maybeCancelWaitForMoreEvents();
return;
}
+ d->sendQueuedUserInputEvents();
d->processPostedEvents();
d->maybeCancelWaitForMoreEvents();
}
diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.h b/src/plugins/platforms/cocoa/qcocoaglcontext.h
index eefb1cd0fb..c1041ac2da 100644
--- a/src/plugins/platforms/cocoa/qcocoaglcontext.h
+++ b/src/plugins/platforms/cocoa/qcocoaglcontext.h
@@ -80,7 +80,7 @@ private:
NSOpenGLContext *m_shareContext = nil;
QSurfaceFormat m_format;
bool m_didCheckForSoftwareContext = false;
- QVarLengthArray<QMacScopedObserver, 3> m_updateObservers;
+ QVarLengthArray<QMacNotificationObserver, 3> m_updateObservers;
QAtomicInt m_needsUpdate = false;
};
diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.mm b/src/plugins/platforms/cocoa/qcocoaglcontext.mm
index fe1fc31553..0f8fec0548 100644
--- a/src/plugins/platforms/cocoa/qcocoaglcontext.mm
+++ b/src/plugins/platforms/cocoa/qcocoaglcontext.mm
@@ -404,13 +404,13 @@ bool QCocoaGLContext::setDrawable(QPlatformSurface *surface)
m_updateObservers.clear();
if (view.layer) {
- m_updateObservers.append(QMacScopedObserver(view, NSViewFrameDidChangeNotification, updateCallback));
- m_updateObservers.append(QMacScopedObserver(view.window, NSWindowDidChangeScreenNotification, updateCallback));
+ m_updateObservers.append(QMacNotificationObserver(view, NSViewFrameDidChangeNotification, updateCallback));
+ m_updateObservers.append(QMacNotificationObserver(view.window, NSWindowDidChangeScreenNotification, updateCallback));
} else {
- m_updateObservers.append(QMacScopedObserver(view, NSViewGlobalFrameDidChangeNotification, updateCallback));
+ m_updateObservers.append(QMacNotificationObserver(view, NSViewGlobalFrameDidChangeNotification, updateCallback));
}
- m_updateObservers.append(QMacScopedObserver([NSApplication sharedApplication],
+ m_updateObservers.append(QMacNotificationObserver([NSApplication sharedApplication],
NSApplicationDidChangeScreenParametersNotification, updateCallback));
// If any of the observers fire at this point it's fine. We check the
diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm
index 1b184cd60f..c9eafa81d0 100644
--- a/src/plugins/platforms/cocoa/qcocoahelpers.mm
+++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm
@@ -297,13 +297,12 @@ Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum)
*/
Qt::MouseButton cocoaButton2QtButton(NSEvent *event)
{
- switch (event.type) {
- case NSEventTypeMouseMoved:
+ if (cocoaEvent2QtMouseEvent(event) == QEvent::MouseMove)
return Qt::NoButton;
+ switch (event.type) {
case NSEventTypeRightMouseUp:
case NSEventTypeRightMouseDown:
- case NSEventTypeRightMouseDragged:
return Qt::RightButton;
default:
diff --git a/src/plugins/platforms/cocoa/qcocoamenu.h b/src/plugins/platforms/cocoa/qcocoamenu.h
index 34d8428188..a957710a88 100644
--- a/src/plugins/platforms/cocoa/qcocoamenu.h
+++ b/src/plugins/platforms/cocoa/qcocoamenu.h
@@ -92,6 +92,9 @@ public:
bool isOpen() const;
void setIsOpen(bool isOpen);
+ bool isAboutToShow() const;
+ void setIsAboutToShow(bool isAbout);
+
void timerEvent(QTimerEvent *e) override;
void syncMenuItem_helper(QPlatformMenuItem *menuItem, bool menubarUpdate);
@@ -111,6 +114,7 @@ private:
bool m_parentEnabled:1;
bool m_visible:1;
bool m_isOpen:1;
+ bool m_isAboutToShow:1;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoamenu.mm b/src/plugins/platforms/cocoa/qcocoamenu.mm
index f34988721d..8c4fca0d29 100644
--- a/src/plugins/platforms/cocoa/qcocoamenu.mm
+++ b/src/plugins/platforms/cocoa/qcocoamenu.mm
@@ -178,6 +178,16 @@ void QCocoaMenu::setIsOpen(bool isOpen)
m_isOpen = isOpen;
}
+bool QCocoaMenu::isAboutToShow() const
+{
+ return m_isAboutToShow;
+}
+
+void QCocoaMenu::setIsAboutToShow(bool isAbout)
+{
+ m_isAboutToShow = isAbout;
+}
+
void QCocoaMenu::removeMenuItem(QPlatformMenuItem *menuItem)
{
QMacAutoReleasePool pool;
diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.mm b/src/plugins/platforms/cocoa/qcocoamenuitem.mm
index 32fc3ab660..c35cf6d799 100644
--- a/src/plugins/platforms/cocoa/qcocoamenuitem.mm
+++ b/src/plugins/platforms/cocoa/qcocoamenuitem.mm
@@ -141,6 +141,12 @@ void QCocoaMenuItem::setMenu(QPlatformMenu *menu)
if (menu == m_menu)
return;
+ bool setAttached = false;
+ if ([m_native.menu isKindOfClass:[QCocoaNSMenu class]]) {
+ auto parentMenu = static_cast<QCocoaNSMenu *>(m_native.menu);
+ setAttached = parentMenu.platformMenu && parentMenu.platformMenu->isAboutToShow();
+ }
+
if (m_menu && m_menu->menuParent() == this) {
m_menu->setMenuParent(nullptr);
// Free the menu from its parent's influence
@@ -154,6 +160,8 @@ void QCocoaMenuItem::setMenu(QPlatformMenu *menu)
if (m_menu) {
m_menu->setMenuParent(this);
m_menu->propagateEnabledState(isEnabled());
+ if (setAttached)
+ m_menu->setAttachedItem(m_native);
} else {
// we previously had a menu, but no longer
// clear out our item so the nexy sync() call builds a new one
diff --git a/src/plugins/platforms/cocoa/qcocoansmenu.mm b/src/plugins/platforms/cocoa/qcocoansmenu.mm
index 65b0832d9f..c51460282a 100644
--- a/src/plugins/platforms/cocoa/qcocoansmenu.mm
+++ b/src/plugins/platforms/cocoa/qcocoansmenu.mm
@@ -195,7 +195,9 @@ static NSString *qt_mac_removePrivateUnicode(NSString *string)
return;
platformMenu->setIsOpen(true);
+ platformMenu->setIsAboutToShow(true);
emit platformMenu->aboutToShow();
+ platformMenu->setIsAboutToShow(false);
}
- (void)menuDidClose:(NSMenu *)menu
diff --git a/src/plugins/platforms/cocoa/qcocoatheme.h b/src/plugins/platforms/cocoa/qcocoatheme.h
index c42fa7d2e8..788b616e78 100644
--- a/src/plugins/platforms/cocoa/qcocoatheme.h
+++ b/src/plugins/platforms/cocoa/qcocoatheme.h
@@ -45,6 +45,8 @@
Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QCocoaThemeAppAppearanceObserver));
+#include <QtCore/private/qcore_mac_p.h>
+
QT_BEGIN_NAMESPACE
class QPalette;
@@ -82,9 +84,10 @@ public:
private:
mutable QPalette *m_systemPalette;
+ QMacNotificationObserver m_systemColorObserver;
mutable QHash<QPlatformTheme::Palette, QPalette*> m_palettes;
mutable QHash<QPlatformTheme::Font, QFont*> m_fonts;
- QT_MANGLE_NAMESPACE(QCocoaThemeAppAppearanceObserver) *m_appearanceObserver;
+ QMacKeyValueObserver m_appearanceObserver;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoatheme.mm b/src/plugins/platforms/cocoa/qcocoatheme.mm
index efe670abed..7c10456824 100644
--- a/src/plugins/platforms/cocoa/qcocoatheme.mm
+++ b/src/plugins/platforms/cocoa/qcocoatheme.mm
@@ -80,70 +80,32 @@
#include <CoreServices/CoreServices.h>
-#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14)
-@interface QT_MANGLE_NAMESPACE(QCocoaThemeAppAppearanceObserver) : NSObject
-@property (readonly, nonatomic) QCocoaTheme *theme;
-- (instancetype)initWithTheme:(QCocoaTheme *)theme;
-@end
-
-QT_NAMESPACE_ALIAS_OBJC_CLASS(QCocoaThemeAppAppearanceObserver);
-
-@implementation QCocoaThemeAppAppearanceObserver
-- (instancetype)initWithTheme:(QCocoaTheme *)theme
-{
- if ((self = [super init])) {
- _theme = theme;
- [NSApp addObserver:self forKeyPath:@"effectiveAppearance" options:NSKeyValueObservingOptionNew context:nullptr];
- }
- return self;
-}
-
-- (void)dealloc
-{
- [NSApp removeObserver:self forKeyPath:@"effectiveAppearance"];
- [super dealloc];
-}
-
-- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
- change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context
-{
- Q_UNUSED(change);
- Q_UNUSED(context);
-
- Q_ASSERT(object == NSApp);
- Q_ASSERT([keyPath isEqualToString:@"effectiveAppearance"]);
-
- if (__builtin_available(macOS 10.14, *))
- NSAppearance.currentAppearance = NSApp.effectiveAppearance;
-
- self.theme->handleSystemThemeChange();
-}
-@end
-#endif // QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14)
-
QT_BEGIN_NAMESPACE
const char *QCocoaTheme::name = "cocoa";
QCocoaTheme::QCocoaTheme()
- : m_systemPalette(nullptr), m_appearanceObserver(nil)
+ : m_systemPalette(nullptr)
{
#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14)
- if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave)
- m_appearanceObserver = [[QCocoaThemeAppAppearanceObserver alloc] initWithTheme:this];
+ if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave) {
+ m_appearanceObserver = QMacKeyValueObserver(NSApp, @"effectiveAppearance", [this] {
+ if (__builtin_available(macOS 10.14, *))
+ NSAppearance.currentAppearance = NSApp.effectiveAppearance;
+
+ handleSystemThemeChange();
+ });
+ }
#endif
- [[NSNotificationCenter defaultCenter] addObserverForName:NSSystemColorsDidChangeNotification
- object:nil queue:nil usingBlock:^(NSNotification *) {
+ m_systemColorObserver = QMacNotificationObserver(nil,
+ NSSystemColorsDidChangeNotification, [this] {
handleSystemThemeChange();
- }];
+ });
}
QCocoaTheme::~QCocoaTheme()
{
- if (m_appearanceObserver)
- [m_appearanceObserver release];
-
reset();
qDeleteAll(m_fonts);
}
diff --git a/src/plugins/platforms/cocoa/qcocoavulkaninstance.mm b/src/plugins/platforms/cocoa/qcocoavulkaninstance.mm
index b00fde6c6f..7ce78ee738 100644
--- a/src/plugins/platforms/cocoa/qcocoavulkaninstance.mm
+++ b/src/plugins/platforms/cocoa/qcocoavulkaninstance.mm
@@ -54,7 +54,7 @@ QCocoaVulkanInstance::~QCocoaVulkanInstance()
void QCocoaVulkanInstance::createOrAdoptInstance()
{
- initInstance(m_instance, QByteArrayList());
+ initInstance(m_instance, QByteArrayList() << QByteArrayLiteral("VK_MVK_macos_surface"));
}
VkSurfaceKHR *QCocoaVulkanInstance::createSurface(QWindow *window)
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
index 67b0cbdfe9..363a026e71 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.mm
+++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
@@ -1011,16 +1011,16 @@ void QCocoaWindow::setMask(const QRegion &region)
} else {
m_view.layer.mask = nil;
}
- }
-
- if (isContentView()) {
- // Setting the mask requires invalidating the NSWindow shadow, but that needs
- // to happen after the backingstore has been redrawn, so that AppKit can pick
- // up the new window shape based on the backingstore content. Doing a display
- // directly here is not an option, as the window might not be exposed at this
- // time, and so would not result in an updated backingstore.
- m_needsInvalidateShadow = true;
- [m_view setNeedsDisplay:YES];
+ } else {
+ if (isContentView()) {
+ // Setting the mask requires invalidating the NSWindow shadow, but that needs
+ // to happen after the backingstore has been redrawn, so that AppKit can pick
+ // up the new window shape based on the backingstore content. Doing a display
+ // directly here is not an option, as the window might not be exposed at this
+ // time, and so would not result in an updated backingstore.
+ m_needsInvalidateShadow = true;
+ [m_view setNeedsDisplay:YES];
+ }
}
}
diff --git a/src/plugins/platforms/cocoa/qnsview_mouse.mm b/src/plugins/platforms/cocoa/qnsview_mouse.mm
index 4408385aa8..7c566442f0 100644
--- a/src/plugins/platforms/cocoa/qnsview_mouse.mm
+++ b/src/plugins/platforms/cocoa/qnsview_mouse.mm
@@ -282,20 +282,19 @@
nativeDrag->setLastMouseEvent(theEvent, self);
const auto modifiers = [QNSView convertKeyModifiers:theEvent.modifierFlags];
- const auto buttons = currentlyPressedMouseButtons();
auto button = cocoaButton2QtButton(theEvent);
if (button == Qt::LeftButton && m_sendUpAsRightButton)
button = Qt::RightButton;
const auto eventType = cocoaEvent2QtMouseEvent(theEvent);
if (eventType == QEvent::MouseMove)
- qCDebug(lcQpaMouse) << eventType << "at" << qtWindowPoint << "with" << buttons;
+ qCDebug(lcQpaMouse) << eventType << "at" << qtWindowPoint << "with" << m_buttons;
else
- qCInfo(lcQpaMouse) << eventType << "of" << button << "at" << qtWindowPoint << "with" << buttons;
+ qCInfo(lcQpaMouse) << eventType << "of" << button << "at" << qtWindowPoint << "with" << m_buttons;
QWindowSystemInterface::handleMouseEvent(targetView->m_platformWindow->window(),
timestamp, qtWindowPoint, qtScreenPoint,
- buttons, button, eventType, modifiers);
+ m_buttons, button, eventType, modifiers);
}
- (bool)handleMouseDownEvent:(NSEvent *)theEvent
diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dhelpers.h b/src/plugins/platforms/direct2d/qwindowsdirect2dhelpers.h
index babe646fd8..6d880e24cf 100644
--- a/src/plugins/platforms/direct2d/qwindowsdirect2dhelpers.h
+++ b/src/plugins/platforms/direct2d/qwindowsdirect2dhelpers.h
@@ -69,12 +69,15 @@ inline D2D1_RECT_F to_d2d_rect_f(const QRectF &qrect)
inline D2D1_SIZE_U to_d2d_size_u(const QSizeF &qsize)
{
- return D2D1::SizeU(qsize.width(), qsize.height());
+
+ return D2D1::SizeU(UINT32(qRound(qsize.width())),
+ UINT32(qRound(qsize.height())));
}
inline D2D1_SIZE_U to_d2d_size_u(const QSize &qsize)
{
- return D2D1::SizeU(qsize.width(), qsize.height());
+ return D2D1::SizeU(UINT32(qsize.width()),
+ UINT32(qsize.height()));
}
inline D2D1_POINT_2F to_d2d_point_2f(const QPointF &qpoint)
diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.cpp
index 86c863ec50..0cf05cb0ac 100644
--- a/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.cpp
+++ b/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.cpp
@@ -91,7 +91,7 @@ static QVersionNumber systemD2DVersion()
if (VerQueryValue(info.constData(), __TEXT("\\"),
reinterpret_cast<void **>(&fi), &size) && size) {
- const VS_FIXEDFILEINFO *verInfo = reinterpret_cast<const VS_FIXEDFILEINFO *>(fi);
+ const auto *verInfo = reinterpret_cast<const VS_FIXEDFILEINFO *>(fi);
return QVersionNumber{HIWORD(verInfo->dwFileVersionMS), LOWORD(verInfo->dwFileVersionMS),
HIWORD(verInfo->dwFileVersionLS), LOWORD(verInfo->dwFileVersionLS)};
}
@@ -140,7 +140,7 @@ QWindowsDirect2DIntegration *QWindowsDirect2DIntegration::create(const QStringLi
return nullptr;
}
- QWindowsDirect2DIntegration *integration = new QWindowsDirect2DIntegration(paramList);
+ auto *integration = new QWindowsDirect2DIntegration(paramList);
if (!integration->init()) {
delete integration;
diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp
index 1b82db0b37..cf3e88d54e 100644
--- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp
+++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp
@@ -253,7 +253,7 @@ struct D2DVectorPathCache {
static void cleanup_func(QPaintEngineEx *engine, void *data) {
Q_UNUSED(engine);
- D2DVectorPathCache *e = static_cast<D2DVectorPathCache *>(data);
+ auto *e = static_cast<D2DVectorPathCache *>(data);
delete e;
}
};
@@ -689,7 +689,7 @@ public:
*needsEmulation = true;
} else {
ComPtr<ID2D1LinearGradientBrush> linear;
- const QLinearGradient *qlinear = static_cast<const QLinearGradient *>(newBrush.gradient());
+ const auto *qlinear = static_cast<const QLinearGradient *>(newBrush.gradient());
D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES linearGradientBrushProperties;
ComPtr<ID2D1GradientStopCollection> gradientStopCollection;
@@ -727,7 +727,7 @@ public:
*needsEmulation = true;
} else {
ComPtr<ID2D1RadialGradientBrush> radial;
- const QRadialGradient *qradial = static_cast<const QRadialGradient *>(newBrush.gradient());
+ const auto *qradial = static_cast<const QRadialGradient *>(newBrush.gradient());
D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES radialGradientBrushProperties;
ComPtr<ID2D1GradientStopCollection> gradientStopCollection;
@@ -807,7 +807,7 @@ public:
: nullptr;
if (cacheEntry) {
- D2DVectorPathCache *e = static_cast<D2DVectorPathCache *>(cacheEntry->data);
+ auto *e = static_cast<D2DVectorPathCache *>(cacheEntry->data);
if (alias && e->aliased)
return e->aliased;
else if (!alias && e->antiAliased)
@@ -885,7 +885,7 @@ public:
if (!cacheEntry)
cacheEntry = path.addCacheData(q, new D2DVectorPathCache, D2DVectorPathCache::cleanup_func);
- D2DVectorPathCache *e = static_cast<D2DVectorPathCache *>(cacheEntry->data);
+ auto *e = static_cast<D2DVectorPathCache *>(cacheEntry->data);
if (alias)
e->aliased = geometry;
else
@@ -1481,7 +1481,7 @@ void QWindowsDirect2DPaintEngine::drawPixmap(const QRectF &r,
return;
}
- QWindowsDirect2DPlatformPixmap *pp = static_cast<QWindowsDirect2DPlatformPixmap *>(pm.handle());
+ auto *pp = static_cast<QWindowsDirect2DPlatformPixmap *>(pm.handle());
QWindowsDirect2DBitmap *bitmap = pp->bitmap();
ensurePen();
@@ -1593,7 +1593,7 @@ void QWindowsDirect2DPaintEngine::drawTextItem(const QPointF &p, const QTextItem
Q_D(QWindowsDirect2DPaintEngine);
D2D_TAG(D2DDebugDrawTextItemTag);
- const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
+ const auto &ti = static_cast<const QTextItemInt &>(textItem);
if (ti.glyphs.numGlyphs == 0)
return;
@@ -1686,7 +1686,7 @@ void QWindowsDirect2DPaintEngine::rasterFill(const QVectorPath &path, const QBru
p.setBrush(state()->brush);
p.setPen(state()->pen);
- QPaintEngineEx *extended = static_cast<QPaintEngineEx *>(engine);
+ auto *extended = static_cast<QPaintEngineEx *>(engine);
for (const QPainterClipInfo &info : qAsConst(state()->clipInfo)) {
extended->state()->matrix = info.matrix;
extended->transformChanged();
diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dwindow.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dwindow.cpp
index 239e5f3087..c417daaeae 100644
--- a/src/plugins/platforms/direct2d/qwindowsdirect2dwindow.cpp
+++ b/src/plugins/platforms/direct2d/qwindowsdirect2dwindow.cpp
@@ -310,7 +310,7 @@ void QWindowsDirect2DWindow::setupBitmap()
QWindowsDirect2DPaintEngine::Flags flags = QWindowsDirect2DPaintEngine::NoFlag;
if (!m_directRendering)
flags |= QWindowsDirect2DPaintEngine::TranslucentTopLevelWindow;
- QWindowsDirect2DPlatformPixmap *pp = new QWindowsDirect2DPlatformPixmap(QPlatformPixmap::PixmapType,
+ auto *pp = new QWindowsDirect2DPlatformPixmap(QPlatformPixmap::PixmapType,
flags,
m_bitmap.data());
m_pixmap.reset(new QPixmap(pp));
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.cpp
index 2e84915c80..07b2de7c58 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.cpp
@@ -66,7 +66,7 @@ QAtomicInt running;
void EventReader::run()
{
xcb_generic_event_t *event = nullptr;
- while (running.load() && (event = xcb_wait_for_event(m_integration->connection()))) {
+ while (running.loadRelaxed() && (event = xcb_wait_for_event(m_integration->connection()))) {
uint response_type = event->response_type & ~0x80;
switch (response_type) {
case XCB_CLIENT_MESSAGE: {
diff --git a/src/plugins/platforms/ios/quiview_accessibility.mm b/src/plugins/platforms/ios/quiview_accessibility.mm
index a3f4156a59..458ddcc9b8 100644
--- a/src/plugins/platforms/ios/quiview_accessibility.mm
+++ b/src/plugins/platforms/ios/quiview_accessibility.mm
@@ -101,6 +101,8 @@
- (id)accessibilityElementAtIndex:(NSInteger)index
{
[self initAccessibility];
+ if (index >= [m_accessibleElements count])
+ return nil;
return m_accessibleElements[index];
}
@@ -110,4 +112,10 @@
return [m_accessibleElements indexOfObject:element];
}
+- (NSArray *)accessibilityElements
+{
+ [self initAccessibility];
+ return m_accessibleElements;
+}
+
@end
diff --git a/src/plugins/platforms/wasm/qwasmbackingstore.cpp b/src/plugins/platforms/wasm/qwasmbackingstore.cpp
index 5b40c44807..e8eda2605f 100644
--- a/src/plugins/platforms/wasm/qwasmbackingstore.cpp
+++ b/src/plugins/platforms/wasm/qwasmbackingstore.cpp
@@ -55,6 +55,12 @@ QWasmBackingStore::~QWasmBackingStore()
{
}
+void QWasmBackingStore::destroy()
+{
+ if (m_texture->isCreated())
+ m_texture->destroy();
+}
+
QPaintDevice *QWasmBackingStore::paintDevice()
{
return &m_image;
diff --git a/src/plugins/platforms/wasm/qwasmbackingstore.h b/src/plugins/platforms/wasm/qwasmbackingstore.h
index 6ffa262e3d..4bca83c457 100644
--- a/src/plugins/platforms/wasm/qwasmbackingstore.h
+++ b/src/plugins/platforms/wasm/qwasmbackingstore.h
@@ -44,6 +44,7 @@ class QWasmBackingStore : public QPlatformBackingStore
public:
QWasmBackingStore(QWasmCompositor *compositor, QWindow *window);
~QWasmBackingStore();
+ void destroy();
QPaintDevice *paintDevice() override;
diff --git a/src/plugins/platforms/wasm/qwasmcompositor.cpp b/src/plugins/platforms/wasm/qwasmcompositor.cpp
index e6a69c4814..6d211667be 100644
--- a/src/plugins/platforms/wasm/qwasmcompositor.cpp
+++ b/src/plugins/platforms/wasm/qwasmcompositor.cpp
@@ -37,6 +37,7 @@
#include <QtGui/qopenglcontext.h>
#include <QtGui/qopenglfunctions.h>
#include <QtGui/qopengltextureblitter.h>
+#include <QtGui/qoffscreensurface.h>
#include <QtGui/qpainter.h>
#include <private/qpixmapcache_p.h>
@@ -71,6 +72,28 @@ QWasmCompositor::QWasmCompositor(QWasmScreen *screen)
QWasmCompositor::~QWasmCompositor()
{
delete m_frameBuffer;
+ destroy();
+}
+
+void QWasmCompositor::destroy()
+{
+ // Destroy OpenGL resources. This is done here in a separate function
+ // which can be called while screen() still returns a valid screen
+ // (which it might not, during destruction). A valid QScreen is
+ // a requirement for QOffscreenSurface on Wasm since the native
+ // context is tied to a single canvas.
+ if (m_context) {
+ QOffscreenSurface offScreenSurface(screen()->screen());
+ offScreenSurface.setFormat(m_context->format());
+ offScreenSurface.create();
+ m_context->makeCurrent(&offScreenSurface);
+ for (QWasmWindow *window : m_windowStack)
+ window->destroy();
+ m_blitter.reset(nullptr);
+ m_context.reset(nullptr);
+ }
+
+ m_isEnabled = false; // prevent frame() from creating a new m_context
}
void QWasmCompositor::setEnabled(bool enabled)
@@ -251,10 +274,13 @@ void QWasmCompositor::blit(QOpenGLTextureBlitter *blitter, QWasmScreen *screen,
void QWasmCompositor::drawWindowContent(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window)
{
QWasmBackingStore *backingStore = window->backingStore();
+ if (!backingStore)
+ return;
QOpenGLTexture const *texture = backingStore->getUpdatedTexture();
-
- blit(blitter, screen, texture, window->geometry());
+ QPoint windowCanvasPosition = window->geometry().topLeft() - screen->geometry().topLeft();
+ QRect windowCanvasGeometry = QRect(windowCanvasPosition, window->geometry().size());
+ blit(blitter, screen, texture, windowCanvasGeometry);
}
QPalette QWasmCompositor::makeWindowPalette()
@@ -650,7 +676,7 @@ void QWasmCompositor::frame()
m_needComposit = false;
- if (m_windowStack.empty() || !screen())
+ if (!m_isEnabled || m_windowStack.empty() || !screen())
return;
QWasmWindow *someWindow = nullptr;
@@ -673,7 +699,9 @@ void QWasmCompositor::frame()
m_context->create();
}
- m_context->makeCurrent(someWindow->window());
+ bool ok = m_context->makeCurrent(someWindow->window());
+ if (!ok)
+ return;
if (!m_blitter->isCreated())
m_blitter->create();
diff --git a/src/plugins/platforms/wasm/qwasmcompositor.h b/src/plugins/platforms/wasm/qwasmcompositor.h
index 3104573073..98f4a79b27 100644
--- a/src/plugins/platforms/wasm/qwasmcompositor.h
+++ b/src/plugins/platforms/wasm/qwasmcompositor.h
@@ -64,6 +64,7 @@ class QWasmCompositor : public QObject
public:
QWasmCompositor(QWasmScreen *screen);
~QWasmCompositor();
+ void destroy();
enum QWasmSubControl {
SC_None = 0x00000000,
diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp
index 6a02a457a0..3895646b89 100644
--- a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp
+++ b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp
@@ -337,8 +337,6 @@ QWasmEventTranslator::QWasmEventTranslator(QWasmScreen *screen)
void QWasmEventTranslator::initEventHandlers()
{
- qDebug() << "QWasmEventTranslator::initEventHandlers";
-
QByteArray _canvasId = screen()->canvasId().toUtf8();
const char *canvasId = _canvasId.constData();
@@ -377,9 +375,6 @@ void QWasmEventTranslator::initEventHandlers()
emscripten_set_touchend_callback(canvasId, (void *)this, 1, &touchCallback);
emscripten_set_touchmove_callback(canvasId, (void *)this, 1, &touchCallback);
emscripten_set_touchcancel_callback(canvasId, (void *)this, 1, &touchCallback);
-
- emscripten_set_resize_callback(nullptr, (void *)this, 1, uiEvent_cb); // Note: handles browser window resize
-
}
template <typename Event>
@@ -557,9 +552,12 @@ void QWasmEventTranslator::processMouse(int eventType, const EmscriptenMouseEven
Qt::KeyboardModifiers modifiers = translateMouseEventModifier(mouseEvent);
QWindow *window2 = screen()->compositor()->windowAt(globalPoint, 5);
- if (window2 == nullptr)
- return;
- lastWindow = window2;
+
+ if (window2 == nullptr) {
+ window2 = lastWindow;
+ } else {
+ lastWindow = window2;
+ }
QPoint localPoint = window2->mapFromGlobal(globalPoint);
bool interior = window2->geometry().contains(globalPoint);
@@ -621,7 +619,7 @@ void QWasmEventTranslator::processMouse(int eventType, const EmscriptenMouseEven
}
if (resizeMode != QWasmWindow::ResizeNone && !(htmlWindow->m_windowState & Qt::WindowFullScreen)) {
- QPoint delta = QPoint(mouseEvent->canvasX, mouseEvent->canvasY) - resizePoint;
+ QPoint delta = QPoint(mouseEvent->targetX, mouseEvent->targetY) - resizePoint;
resizeWindow(draggedWindow, resizeMode, resizeStartRect, delta);
}
}
@@ -911,19 +909,4 @@ bool QWasmEventTranslator::processKeyboard(int eventType, const EmscriptenKeyboa
return accepted;
}
-int QWasmEventTranslator::uiEvent_cb(int eventType, const EmscriptenUiEvent *e, void *userData)
-{
- Q_UNUSED(e)
- QWasmEventTranslator *eventTranslator = static_cast<QWasmEventTranslator *>(userData);
-
- if (eventType == EMSCRIPTEN_EVENT_RESIZE) {
- // This resize event is called when the HTML window is resized. Depending
- // on the page layout the the canvas might also have been resized, so we
- // update the Qt screen size (and canvas render size).
- eventTranslator->screen()->updateQScreenAndCanvasRenderSize();
- }
-
- return 0;
-}
-
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.h b/src/plugins/platforms/wasm/qwasmeventtranslator.h
index d6043072ba..1655b7226a 100644
--- a/src/plugins/platforms/wasm/qwasmeventtranslator.h
+++ b/src/plugins/platforms/wasm/qwasmeventtranslator.h
@@ -57,8 +57,6 @@ public:
static int touchCallback(int eventType, const EmscriptenTouchEvent *ev, void *userData);
- static int uiEvent_cb(int eventType, const EmscriptenUiEvent *e, void *userData);
-
void processEvents();
void initEventHandlers();
int handleTouch(int eventType, const EmscriptenTouchEvent *touchEvent);
diff --git a/src/plugins/platforms/wasm/qwasmintegration.cpp b/src/plugins/platforms/wasm/qwasmintegration.cpp
index 31b9104de1..116612c286 100644
--- a/src/plugins/platforms/wasm/qwasmintegration.cpp
+++ b/src/plugins/platforms/wasm/qwasmintegration.cpp
@@ -35,6 +35,7 @@
#include "qwasmtheme.h"
#include "qwasmclipboard.h"
#include "qwasmservices.h"
+#include "qwasmoffscreensurface.h"
#include "qwasmwindow.h"
#ifndef QT_NO_OPENGL
@@ -123,6 +124,21 @@ QWasmIntegration::QWasmIntegration()
}
emscripten::val::global("window").set("onbeforeunload", val::module_property("qtBrowserBeforeUnload"));
+
+ // install browser window resize handler
+ auto onWindowResize = [](int eventType, const EmscriptenUiEvent *e, void *userData) -> int {
+ Q_UNUSED(eventType);
+ Q_UNUSED(e);
+ Q_UNUSED(userData);
+
+ // This resize event is called when the HTML window is resized. Depending
+ // on the page layout the the canvas(es) might also have been resized, so we
+ // update the Qt screen sizes (and canvas render sizes).
+ if (QWasmIntegration *integration = QWasmIntegration::get())
+ integration->resizeAllScreens();
+ return 0;
+ };
+ emscripten_set_resize_callback(nullptr, nullptr, 1, onWindowResize);
}
QWasmIntegration::~QWasmIntegration()
@@ -193,6 +209,11 @@ QPlatformInputContext *QWasmIntegration::inputContext() const
return m_inputContext.data();
}
+QPlatformOffscreenSurface *QWasmIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const
+{
+ return new QWasmOffscrenSurface(surface);
+}
+
QPlatformFontDatabase *QWasmIntegration::fontDatabase() const
{
if (m_fontDb == nullptr)
@@ -257,7 +278,9 @@ void QWasmIntegration::addScreen(const QString &canvasId)
void QWasmIntegration::removeScreen(const QString &canvasId)
{
- QWindowSystemInterface::handleScreenRemoved(m_screens.take(canvasId));
+ QWasmScreen *exScreen = m_screens.take(canvasId);
+ exScreen->destroy(); // clean up before deleting the screen
+ QWindowSystemInterface::handleScreenRemoved(exScreen);
}
void QWasmIntegration::resizeScreen(const QString &canvasId)
@@ -275,4 +298,11 @@ void QWasmIntegration::updateDpi()
QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen->screen(), dpiValue, dpiValue);
}
+void QWasmIntegration::resizeAllScreens()
+{
+ qDebug() << "resizeAllScreens";
+ for (QWasmScreen *screen : m_screens)
+ screen->updateQScreenAndCanvasRenderSize();
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmintegration.h b/src/plugins/platforms/wasm/qwasmintegration.h
index c04c0eaecc..2102f5c226 100644
--- a/src/plugins/platforms/wasm/qwasmintegration.h
+++ b/src/plugins/platforms/wasm/qwasmintegration.h
@@ -66,6 +66,7 @@ public:
#ifndef QT_NO_OPENGL
QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const override;
#endif
+ QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const override;
QPlatformFontDatabase *fontDatabase() const override;
QAbstractEventDispatcher *createEventDispatcher() const override;
QVariant styleHint(QPlatformIntegration::StyleHint hint) const override;
@@ -85,6 +86,7 @@ public:
void addScreen(const QString &canvasId);
void removeScreen(const QString &canvasId);
void resizeScreen(const QString &canvasId);
+ void resizeAllScreens();
void updateDpi();
private:
diff --git a/src/plugins/platforms/wasm/qwasmoffscreensurface.cpp b/src/plugins/platforms/wasm/qwasmoffscreensurface.cpp
new file mode 100644
index 0000000000..a205e5ddea
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmoffscreensurface.cpp
@@ -0,0 +1,41 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwasmoffscreensurface.h"
+
+QWasmOffscrenSurface::QWasmOffscrenSurface(QOffscreenSurface *offscreenSurface)
+ :QPlatformOffscreenSurface(offscreenSurface)
+{
+
+}
+
+QWasmOffscrenSurface::~QWasmOffscrenSurface()
+{
+
+}
diff --git a/src/plugins/platforms/wasm/qwasmoffscreensurface.h b/src/plugins/platforms/wasm/qwasmoffscreensurface.h
new file mode 100644
index 0000000000..9d3e805be0
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmoffscreensurface.h
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWASMOFFSCREENSURFACE_H
+#define QWASMOFFSCREENSURFACE_H
+
+#include <qpa/qplatformoffscreensurface.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOffscreenSurface;
+class QWasmOffscrenSurface : public QPlatformOffscreenSurface
+{
+public:
+ explicit QWasmOffscrenSurface(QOffscreenSurface *offscreenSurface);
+ ~QWasmOffscrenSurface();
+private:
+
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp
index 1658f32f9e..62087f54bd 100644
--- a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp
+++ b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp
@@ -49,40 +49,31 @@ QWasmOpenGLContext::QWasmOpenGLContext(const QSurfaceFormat &format)
QWasmOpenGLContext::~QWasmOpenGLContext()
{
- if (m_context)
+ if (m_context) {
emscripten_webgl_destroy_context(m_context);
+ m_context = 0;
+ }
}
-void QWasmOpenGLContext::maybeRecreateEmscriptenContext(QPlatformSurface *surface)
+bool QWasmOpenGLContext::maybeCreateEmscriptenContext(QPlatformSurface *surface)
{
- // Native emscripten contexts are tied to a single surface. Recreate
- // the context if the surface is changed.
- if (surface != m_surface) {
- m_surface = surface;
-
- // Destroy existing context
- if (m_context)
- emscripten_webgl_destroy_context(m_context);
-
- // Create new context
- const QString canvasId = QWasmScreen::get(surface->screen())->canvasId();
- m_context = createEmscriptenContext(canvasId, m_requestedFormat);
-
- // Register context-lost callback.
- auto callback = [](int eventType, const void *reserved, void *userData) -> EM_BOOL
- {
- Q_UNUSED(eventType);
- Q_UNUSED(reserved);
- // The application may get contex-lost if e.g. moved to the background. Set
- // m_contextLost which will make isValid() return false. Application code will
- // then detect this and recrate the the context, resulting in a new QWasmOpenGLContext
- // instance.
- reinterpret_cast<QWasmOpenGLContext *>(userData)->m_contextLost = true;
- return true;
- };
- bool capture = true;
- emscripten_set_webglcontextlost_callback(canvasId.toLocal8Bit().constData(), this, capture, callback);
- }
+ // Native emscripten/WebGL contexts are tied to a single screen/canvas. The first
+ // call to this function creates a native canvas for the given screen, subsequent
+ // calls verify that the surface is on/off the same screen.
+ QPlatformScreen *screen = surface->screen();
+ if (m_context && !screen)
+ return false; // Alternative: return true to support makeCurrent on QOffScreenSurface with
+ // no screen. However, Qt likes to substitute QGuiApplication::primaryScreen()
+ // for null screens, which foils this plan.
+ if (!screen)
+ return false;
+ if (m_context)
+ return m_screen == screen;
+
+ QString canvasId = QWasmScreen::get(screen)->canvasId();
+ m_context = createEmscriptenContext(canvasId, m_requestedFormat);
+ m_screen = screen;
+ return true;
}
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE QWasmOpenGLContext::createEmscriptenContext(const QString &canvasId, QSurfaceFormat format)
@@ -125,7 +116,9 @@ GLuint QWasmOpenGLContext::defaultFramebufferObject(QPlatformSurface *surface) c
bool QWasmOpenGLContext::makeCurrent(QPlatformSurface *surface)
{
- maybeRecreateEmscriptenContext(surface);
+ bool ok = maybeCreateEmscriptenContext(surface);
+ if (!ok)
+ return false;
return emscripten_webgl_make_context_current(m_context) == EMSCRIPTEN_RESULT_SUCCESS;
}
@@ -148,7 +141,9 @@ bool QWasmOpenGLContext::isSharing() const
bool QWasmOpenGLContext::isValid() const
{
- return (m_contextLost == false);
+ // Note: we get isValid() calls before we see the surface and can
+ // create a native context, so no context is also a valid state.
+ return !m_context || !emscripten_is_webgl_context_lost(m_context);
}
QFunctionPointer QWasmOpenGLContext::getProcAddress(const char *procName)
diff --git a/src/plugins/platforms/wasm/qwasmopenglcontext.h b/src/plugins/platforms/wasm/qwasmopenglcontext.h
index 126b596a7e..d27007e8ea 100644
--- a/src/plugins/platforms/wasm/qwasmopenglcontext.h
+++ b/src/plugins/platforms/wasm/qwasmopenglcontext.h
@@ -34,6 +34,7 @@
QT_BEGIN_NAMESPACE
+class QPlatformScreen;
class QWasmOpenGLContext : public QPlatformOpenGLContext
{
public:
@@ -50,12 +51,11 @@ public:
QFunctionPointer getProcAddress(const char *procName) override;
private:
- void maybeRecreateEmscriptenContext(QPlatformSurface *surface);
+ bool maybeCreateEmscriptenContext(QPlatformSurface *surface);
static EMSCRIPTEN_WEBGL_CONTEXT_HANDLE createEmscriptenContext(const QString &canvasId, QSurfaceFormat format);
- bool m_contextLost = false;
QSurfaceFormat m_requestedFormat;
- QPlatformSurface *m_surface = nullptr;
+ QPlatformScreen *m_screen = nullptr;
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE m_context = 0;
};
diff --git a/src/plugins/platforms/wasm/qwasmscreen.cpp b/src/plugins/platforms/wasm/qwasmscreen.cpp
index af50ce7440..f2eabfa486 100644
--- a/src/plugins/platforms/wasm/qwasmscreen.cpp
+++ b/src/plugins/platforms/wasm/qwasmscreen.cpp
@@ -58,7 +58,12 @@ QWasmScreen::QWasmScreen(const QString &canvasId)
QWasmScreen::~QWasmScreen()
{
+ destroy();
+}
+void QWasmScreen::destroy()
+{
+ m_compositor->destroy();
}
QWasmScreen *QWasmScreen::get(QPlatformScreen *screen)
diff --git a/src/plugins/platforms/wasm/qwasmscreen.h b/src/plugins/platforms/wasm/qwasmscreen.h
index 8d0d15f245..fcf693681c 100644
--- a/src/plugins/platforms/wasm/qwasmscreen.h
+++ b/src/plugins/platforms/wasm/qwasmscreen.h
@@ -52,6 +52,7 @@ class QWasmScreen : public QObject, public QPlatformScreen
public:
QWasmScreen(const QString &canvasId);
~QWasmScreen();
+ void destroy();
static QWasmScreen *get(QPlatformScreen *screen);
static QWasmScreen *get(QScreen *screen);
diff --git a/src/plugins/platforms/wasm/qwasmwindow.cpp b/src/plugins/platforms/wasm/qwasmwindow.cpp
index 39797cb09d..594db65cfd 100644
--- a/src/plugins/platforms/wasm/qwasmwindow.cpp
+++ b/src/plugins/platforms/wasm/qwasmwindow.cpp
@@ -65,6 +65,12 @@ QWasmWindow::~QWasmWindow()
m_compositor->removeWindow(this);
}
+void QWasmWindow::destroy()
+{
+ if (m_backingStore)
+ m_backingStore->destroy();
+}
+
void QWasmWindow::initialize()
{
QRect rect = windowGeometry();
diff --git a/src/plugins/platforms/wasm/qwasmwindow.h b/src/plugins/platforms/wasm/qwasmwindow.h
index cbbce99aeb..a098172649 100644
--- a/src/plugins/platforms/wasm/qwasmwindow.h
+++ b/src/plugins/platforms/wasm/qwasmwindow.h
@@ -58,6 +58,7 @@ public:
QWasmWindow(QWindow *w, QWasmCompositor *compositor, QWasmBackingStore *backingStore);
~QWasmWindow();
+ void destroy();
void initialize() override;
diff --git a/src/plugins/platforms/wasm/wasm.pro b/src/plugins/platforms/wasm/wasm.pro
index e8728d9dba..c28df8f893 100644
--- a/src/plugins/platforms/wasm/wasm.pro
+++ b/src/plugins/platforms/wasm/wasm.pro
@@ -20,7 +20,8 @@ SOURCES = \
qwasmopenglcontext.cpp \
qwasmtheme.cpp \
qwasmclipboard.cpp \
- qwasmservices.cpp
+ qwasmservices.cpp \
+ qwasmoffscreensurface.cpp
HEADERS = \
qwasmintegration.h \
@@ -35,7 +36,8 @@ HEADERS = \
qwasmopenglcontext.h \
qwasmtheme.h \
qwasmclipboard.h \
- qwasmservices.h
+ qwasmservices.h \
+ qwasmoffscreensurface.h
wasmfonts.files = \
../../../3rdparty/wasm/Vera.ttf \
diff --git a/src/plugins/platforms/windows/qwin10helpers.cpp b/src/plugins/platforms/windows/qwin10helpers.cpp
index cc17d8798f..9a7fce9cd5 100644
--- a/src/plugins/platforms/windows/qwin10helpers.cpp
+++ b/src/plugins/platforms/windows/qwin10helpers.cpp
@@ -137,7 +137,7 @@ bool qt_windowsIsTabletMode(HWND hwnd)
const wchar_t uiViewSettingsId[] = L"Windows.UI.ViewManagement.UIViewSettings";
HSTRING_HEADER uiViewSettingsIdRefHeader;
HSTRING uiViewSettingsIdHs = nullptr;
- const UINT32 uiViewSettingsIdLen = UINT32(sizeof(uiViewSettingsId) / sizeof(uiViewSettingsId[0]) - 1);
+ const auto uiViewSettingsIdLen = UINT32(sizeof(uiViewSettingsId) / sizeof(uiViewSettingsId[0]) - 1);
if (FAILED(baseComDll.windowsCreateStringReference(uiViewSettingsId, uiViewSettingsIdLen, &uiViewSettingsIdRefHeader, &uiViewSettingsIdHs)))
return false;
diff --git a/src/plugins/platforms/windows/qwindowsbackingstore.cpp b/src/plugins/platforms/windows/qwindowsbackingstore.cpp
index 68807fabdd..bd7bdc55d1 100644
--- a/src/plugins/platforms/windows/qwindowsbackingstore.cpp
+++ b/src/plugins/platforms/windows/qwindowsbackingstore.cpp
@@ -158,7 +158,7 @@ void QWindowsBackingStore::resize(const QSize &size, const QRegion &region)
format = qt_maybeAlphaVersionWithSameDepth(format);
QWindowsNativeImage *oldwni = m_image.data();
- QWindowsNativeImage *newwni = new QWindowsNativeImage(size.width(), size.height(), format);
+ auto *newwni = new QWindowsNativeImage(size.width(), size.height(), format);
if (oldwni && !region.isEmpty()) {
const QImage &oldimg(oldwni->image());
diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp
index 9be6d4ced7..bb349f08a7 100644
--- a/src/plugins/platforms/windows/qwindowscontext.cpp
+++ b/src/plugins/platforms/windows/qwindowscontext.cpp
@@ -77,6 +77,7 @@
#include <QtCore/qoperatingsystemversion.h>
#include <QtCore/qsysinfo.h>
#include <QtCore/qscopedpointer.h>
+#include <QtCore/quuid.h>
#include <QtCore/private/qsystemlibrary_p.h>
#include <QtEventDispatcherSupport/private/qwindowsguieventdispatcher_p.h>
@@ -133,8 +134,8 @@ static inline bool useRTL_Extensions()
#if QT_CONFIG(sessionmanager)
static inline QWindowsSessionManager *platformSessionManager()
{
- QGuiApplicationPrivate *guiPrivate = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(qApp));
- QSessionManagerPrivate *managerPrivate = static_cast<QSessionManagerPrivate*>(QObjectPrivate::get(guiPrivate->session_manager));
+ auto *guiPrivate = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(qApp));
+ auto *managerPrivate = static_cast<QSessionManagerPrivate*>(QObjectPrivate::get(guiPrivate->session_manager));
return static_cast<QWindowsSessionManager *>(managerPrivate->platformSessionManager);
}
@@ -317,6 +318,8 @@ QWindowsContext::~QWindowsContext()
OleUninitialize();
d->m_screenManager.clearScreens(); // Order: Potentially calls back to the windows.
+ if (d->m_displayContext)
+ ReleaseDC(nullptr, d->m_displayContext);
m_instance = nullptr;
}
@@ -542,10 +545,10 @@ QString QWindowsContext::registerWindowClass(QString cname,
// each one has to have window class names with a unique name
// The first instance gets the unmodified name; if the class
// has already been registered by another instance of Qt then
- // add an instance-specific ID, the address of the window proc.
+ // add a UUID.
static int classExists = -1;
- const HINSTANCE appInstance = static_cast<HINSTANCE>(GetModuleHandle(nullptr));
+ const auto appInstance = static_cast<HINSTANCE>(GetModuleHandle(nullptr));
if (classExists == -1) {
WNDCLASS wcinfo;
classExists = GetClassInfo(appInstance, reinterpret_cast<LPCWSTR>(cname.utf16()), &wcinfo);
@@ -553,7 +556,7 @@ QString QWindowsContext::registerWindowClass(QString cname,
}
if (classExists)
- cname += QString::number(reinterpret_cast<quintptr>(proc));
+ cname += QUuid::createUuid().toString();
if (d->m_registeredWindowClassNames.contains(cname)) // already registered in our list
return cname;
@@ -598,7 +601,7 @@ QString QWindowsContext::registerWindowClass(QString cname,
void QWindowsContext::unregisterWindowClasses()
{
- const HINSTANCE appInstance = static_cast<HINSTANCE>(GetModuleHandle(nullptr));
+ const auto appInstance = static_cast<HINSTANCE>(GetModuleHandle(nullptr));
for (const QString &name : qAsConst(d->m_registeredWindowClassNames)) {
if (!UnregisterClass(reinterpret_cast<LPCWSTR>(name.utf16()), appInstance) && QWindowsContext::verbose)
@@ -749,6 +752,12 @@ QWindowsWindow *QWindowsContext::findPlatformWindowAt(HWND parent,
QWindowsWindow *result = nullptr;
const POINT screenPoint = { screenPointIn.x(), screenPointIn.y() };
while (findPlatformWindowHelper(screenPoint, cwex_flags, this, &parent, &result)) {}
+ // QTBUG-40815: ChildWindowFromPointEx() can hit on special windows from
+ // screen recorder applications like ScreenToGif. Fall back to WindowFromPoint().
+ if (result == nullptr) {
+ if (const HWND window = WindowFromPoint(screenPoint))
+ result = findPlatformWindow(window);
+ }
return result;
}
@@ -925,7 +934,7 @@ bool QWindowsContext::systemParametersInfo(unsigned action, unsigned param, void
bool QWindowsContext::systemParametersInfoForScreen(unsigned action, unsigned param, void *out,
const QPlatformScreen *screen)
{
- return systemParametersInfo(action, param, out, screen ? screen->logicalDpi().first : 0);
+ return systemParametersInfo(action, param, out, screen ? unsigned(screen->logicalDpi().first) : 0u);
}
bool QWindowsContext::systemParametersInfoForWindow(unsigned action, unsigned param, void *out,
@@ -944,7 +953,8 @@ bool QWindowsContext::nonClientMetrics(NONCLIENTMETRICS *ncm, unsigned dpi)
bool QWindowsContext::nonClientMetricsForScreen(NONCLIENTMETRICS *ncm,
const QPlatformScreen *screen)
{
- return nonClientMetrics(ncm, screen ? screen->logicalDpi().first : 0);
+ const int dpi = screen ? qRound(screen->logicalDpi().first) : 0;
+ return nonClientMetrics(ncm, unsigned(dpi));
}
bool QWindowsContext::nonClientMetricsForWindow(NONCLIENTMETRICS *ncm, const QPlatformWindow *win)
@@ -1351,7 +1361,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
sessionManager->blocksInteraction();
sessionManager->clearCancellation();
- QGuiApplicationPrivate *qGuiAppPriv = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(qApp));
+ auto *qGuiAppPriv = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(qApp));
qGuiAppPriv->commitData();
if (lParam & ENDSESSION_LOGOFF)
@@ -1369,7 +1379,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
// we receive the message for each toplevel window included internal hidden ones,
// but the aboutToQuit signal should be emitted only once.
- QGuiApplicationPrivate *qGuiAppPriv = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(qApp));
+ auto *qGuiAppPriv = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(qApp));
if (endsession && !qGuiAppPriv->aboutToQuitEmitted) {
qGuiAppPriv->aboutToQuitEmitted = true;
int index = QGuiApplication::staticMetaObject.indexOfSignal("aboutToQuit()");
diff --git a/src/plugins/platforms/windows/qwindowscursor.cpp b/src/plugins/platforms/windows/qwindowscursor.cpp
index 20a8117304..17e8cffb76 100644
--- a/src/plugins/platforms/windows/qwindowscursor.cpp
+++ b/src/plugins/platforms/windows/qwindowscursor.cpp
@@ -752,7 +752,7 @@ QPixmap QWindowsCursor::dragDefaultCursor(Qt::DropAction action) const
&& GetObject(iconInfo.hbmColor, sizeof(BITMAP), &bmColor)
&& bmColor.bmWidth == bmColor.bmWidthBytes / 4) {
const int colorBitsLength = bmColor.bmHeight * bmColor.bmWidthBytes;
- uchar *colorBits = new uchar[colorBitsLength];
+ auto *colorBits = new uchar[colorBitsLength];
GetBitmapBits(iconInfo.hbmColor, colorBitsLength, colorBits);
const QImage colorImage(colorBits, bmColor.bmWidth, bmColor.bmHeight,
bmColor.bmWidthBytes, QImage::Format_ARGB32);
diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
index 57f19b5ff2..b7ab952a1d 100644
--- a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
+++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
@@ -356,7 +356,7 @@ struct FindDialogContext
static BOOL QT_WIN_CALLBACK findDialogEnumWindowsProc(HWND hwnd, LPARAM lParam)
{
- FindDialogContext *context = reinterpret_cast<FindDialogContext *>(lParam);
+ auto *context = reinterpret_cast<FindDialogContext *>(lParam);
DWORD winPid = 0;
GetWindowThreadProcessId(hwnd, &winPid);
if (winPid != context->processId)
@@ -531,7 +531,7 @@ private:
IFileDialogEvents *QWindowsNativeFileDialogEventHandler::create(QWindowsNativeFileDialogBase *nativeFileDialog)
{
IFileDialogEvents *result;
- QWindowsNativeFileDialogEventHandler *eventHandler = new QWindowsNativeFileDialogEventHandler(nativeFileDialog);
+ auto *eventHandler = new QWindowsNativeFileDialogEventHandler(nativeFileDialog);
if (FAILED(eventHandler->QueryInterface(IID_IFileDialogEvents, reinterpret_cast<void **>(&result)))) {
qErrnoWarning("Unable to obtain IFileDialogEvents");
return nullptr;
@@ -1112,7 +1112,7 @@ void QWindowsNativeFileDialogBase::setDefaultSuffixSys(const QString &s)
// If this parameter is non-empty, it will be appended by the dialog for the 'Any files'
// filter ('*'). If this parameter is non-empty and the current filter has a suffix,
// the dialog will append the filter's suffix.
- wchar_t *wSuffix = const_cast<wchar_t *>(reinterpret_cast<const wchar_t *>(s.utf16()));
+ auto *wSuffix = const_cast<wchar_t *>(reinterpret_cast<const wchar_t *>(s.utf16()));
m_fileDialog->SetDefaultExtension(wSuffix);
}
@@ -1125,7 +1125,7 @@ static inline IFileDialog2 *getFileDialog2(IFileDialog *fileDialog)
void QWindowsNativeFileDialogBase::setLabelText(QFileDialogOptions::DialogLabel l, const QString &text)
{
- wchar_t *wText = const_cast<wchar_t *>(reinterpret_cast<const wchar_t *>(text.utf16()));
+ auto *wText = const_cast<wchar_t *>(reinterpret_cast<const wchar_t *>(text.utf16()));
switch (l) {
case QFileDialogOptions::FileName:
m_fileDialog->SetFileNameLabel(wText);
@@ -1770,7 +1770,7 @@ void QWindowsXpNativeFileDialog::doExec(HWND owner)
static int QT_WIN_CALLBACK xpFileDialogGetExistingDirCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
{
- QWindowsXpNativeFileDialog *dialog = reinterpret_cast<QWindowsXpNativeFileDialog *>(lpData);
+ auto *dialog = reinterpret_cast<QWindowsXpNativeFileDialog *>(lpData);
return dialog->existingDirCallback(hwnd, uMsg, lParam);
}
@@ -1843,7 +1843,7 @@ void QWindowsXpNativeFileDialog::populateOpenFileName(OPENFILENAME *ofn, HWND ow
const QList<FilterSpec> specs =
filterSpecs(m_options->nameFilters(), m_options->options() & QFileDialogOptions::HideNameFilterDetails, &totalStringLength);
const int size = specs.size();
- wchar_t *ptr = new wchar_t[totalStringLength + 2 * size + 1];
+ auto *ptr = new wchar_t[totalStringLength + 2 * size + 1];
ofn->lpstrFilter = ptr;
for (const FilterSpec &spec : specs) {
ptr += spec.description.toWCharArray(ptr);
diff --git a/src/plugins/platforms/windows/qwindowsdrag.cpp b/src/plugins/platforms/windows/qwindowsdrag.cpp
index 502c92ef59..3e4c93d47a 100644
--- a/src/plugins/platforms/windows/qwindowsdrag.cpp
+++ b/src/plugins/platforms/windows/qwindowsdrag.cpp
@@ -627,7 +627,7 @@ QWindowsOleDropTarget::Drop(LPDATAOBJECT pDataObj, DWORD grfKeyState,
m_chosenEffect = DROPEFFECT_COPY;
HGLOBAL hData = GlobalAlloc(0, sizeof(DWORD));
if (hData) {
- DWORD *moveEffect = reinterpret_cast<DWORD *>(GlobalLock(hData));
+ auto *moveEffect = reinterpret_cast<DWORD *>(GlobalLock(hData));
*moveEffect = DROPEFFECT_MOVE;
GlobalUnlock(hData);
STGMEDIUM medium;
@@ -704,9 +704,9 @@ Qt::DropAction QWindowsDrag::drag(QDrag *drag)
DWORD resultEffect;
QWindowsDrag::m_canceled = false;
- QWindowsOleDropSource *windowDropSource = new QWindowsOleDropSource(this);
+ auto *windowDropSource = new QWindowsOleDropSource(this);
windowDropSource->createCursors();
- QWindowsDropDataObject *dropDataObject = new QWindowsDropDataObject(dropData);
+ auto *dropDataObject = new QWindowsDropDataObject(dropData);
const Qt::DropActions possibleActions = drag->supportedActions();
const DWORD allowedEffects = translateToWinDragEffects(possibleActions);
qCDebug(lcQpaMime) << '>' << __FUNCTION__ << "possible Actions=0x"
diff --git a/src/plugins/platforms/windows/qwindowseglcontext.cpp b/src/plugins/platforms/windows/qwindowseglcontext.cpp
index 063e81150e..e9f3dc5189 100644
--- a/src/plugins/platforms/windows/qwindowseglcontext.cpp
+++ b/src/plugins/platforms/windows/qwindowseglcontext.cpp
@@ -469,10 +469,10 @@ bool QWindowsEGLContext::makeCurrent(QPlatformSurface *surface)
QWindowsEGLStaticContext::libEGL.eglBindAPI(m_api);
- QWindowsWindow *window = static_cast<QWindowsWindow *>(surface);
+ auto *window = static_cast<QWindowsWindow *>(surface);
window->aboutToMakeCurrent();
int err = 0;
- EGLSurface eglSurface = static_cast<EGLSurface>(window->surface(m_eglConfig, &err));
+ auto eglSurface = static_cast<EGLSurface>(window->surface(m_eglConfig, &err));
if (eglSurface == EGL_NO_SURFACE) {
if (err == EGL_CONTEXT_LOST) {
m_eglContext = EGL_NO_CONTEXT;
@@ -531,9 +531,9 @@ void QWindowsEGLContext::doneCurrent()
void QWindowsEGLContext::swapBuffers(QPlatformSurface *surface)
{
QWindowsEGLStaticContext::libEGL.eglBindAPI(m_api);
- QWindowsWindow *window = static_cast<QWindowsWindow *>(surface);
+ auto *window = static_cast<QWindowsWindow *>(surface);
int err = 0;
- EGLSurface eglSurface = static_cast<EGLSurface>(window->surface(m_eglConfig, &err));
+ auto eglSurface = static_cast<EGLSurface>(window->surface(m_eglConfig, &err));
if (eglSurface == EGL_NO_SURFACE) {
if (err == EGL_CONTEXT_LOST) {
m_eglContext = EGL_NO_CONTEXT;
diff --git a/src/plugins/platforms/windows/qwindowsgdinativeinterface.cpp b/src/plugins/platforms/windows/qwindowsgdinativeinterface.cpp
index 08e11c5e39..f2418b0e60 100644
--- a/src/plugins/platforms/windows/qwindowsgdinativeinterface.cpp
+++ b/src/plugins/platforms/windows/qwindowsgdinativeinterface.cpp
@@ -50,7 +50,7 @@ void *QWindowsGdiNativeInterface::nativeResourceForBackingStore(const QByteArray
qWarning("%s: '%s' requested for null backingstore or backingstore without handle.", __FUNCTION__, resource.constData());
return nullptr;
}
- QWindowsBackingStore *wbs = static_cast<QWindowsBackingStore *>(bs->handle());
+ auto *wbs = static_cast<QWindowsBackingStore *>(bs->handle());
if (resource == "getDC")
return wbs->getDC();
qWarning("%s: Invalid key '%s' requested.", __FUNCTION__, resource.constData());
diff --git a/src/plugins/platforms/windows/qwindowsglcontext.cpp b/src/plugins/platforms/windows/qwindowsglcontext.cpp
index 66dd32d05e..24526bad2c 100644
--- a/src/plugins/platforms/windows/qwindowsglcontext.cpp
+++ b/src/plugins/platforms/windows/qwindowsglcontext.cpp
@@ -1009,7 +1009,7 @@ QOpenGLStaticContext *QOpenGLStaticContext::create(bool softwareRendering)
QScopedPointer<QOpenGLTemporaryContext> temporaryContext;
if (!QOpenGLStaticContext::opengl32.wglGetCurrentContext())
temporaryContext.reset(new QOpenGLTemporaryContext);
- QOpenGLStaticContext *result = new QOpenGLStaticContext;
+ auto *result = new QOpenGLStaticContext;
qCDebug(lcQpaGl) << __FUNCTION__ << *result;
return result;
}
@@ -1051,7 +1051,7 @@ QWindowsGLContext::QWindowsGLContext(QOpenGLStaticContext *staticContext,
qWarning("QWindowsGLContext: Requires a QWGLNativeContext");
return;
}
- QWGLNativeContext handle = nativeHandle.value<QWGLNativeContext>();
+ auto handle = nativeHandle.value<QWGLNativeContext>();
HGLRC wglcontext = handle.context();
HWND wnd = handle.window();
if (!wglcontext || !wnd) {
@@ -1233,7 +1233,7 @@ bool QWindowsGLContext::updateObtainedParams(HDC hdc, int *obtainedSwapInterval)
hasRobustness = exts && strstr(exts, "GL_ARB_robustness");
} else {
typedef const GLubyte * (APIENTRY *glGetStringi_t)(GLenum, GLuint);
- glGetStringi_t glGetStringi = reinterpret_cast<glGetStringi_t>(
+ auto glGetStringi = reinterpret_cast<glGetStringi_t>(
reinterpret_cast<QFunctionPointer>(QOpenGLStaticContext::opengl32.wglGetProcAddress("glGetStringi")));
if (glGetStringi) {
GLint n = 0;
@@ -1305,7 +1305,7 @@ bool QWindowsGLContext::makeCurrent(QPlatformSurface *surface)
Q_ASSERT(surface->surface()->supportsOpenGL());
// Do we already have a DC entry for that window?
- QWindowsWindow *window = static_cast<QWindowsWindow *>(surface);
+ auto *window = static_cast<QWindowsWindow *>(surface);
window->aboutToMakeCurrent();
const HWND hwnd = window->handle();
if (const QOpenGLContextData *contextData = findByHWND(m_windowContexts, hwnd)) {
@@ -1374,7 +1374,7 @@ QFunctionPointer QWindowsGLContext::getProcAddress(const char *procName)
// Even though we use QFunctionPointer, it does not mean the function can be called.
// It will need to be cast to the proper function type with the correct calling
// convention. QFunctionPointer is nothing more than a glorified void* here.
- QFunctionPointer procAddress = reinterpret_cast<QFunctionPointer>(QOpenGLStaticContext::opengl32.wglGetProcAddress(procName));
+ auto procAddress = reinterpret_cast<QFunctionPointer>(QOpenGLStaticContext::opengl32.wglGetProcAddress(procName));
// We support AllGLFunctionsQueryable, which means this function must be able to
// return a function pointer even for functions that are in GL.h and exported
diff --git a/src/plugins/platforms/windows/qwindowsinputcontext.cpp b/src/plugins/platforms/windows/qwindowsinputcontext.cpp
index 71ed33f85b..e681dbb0cb 100644
--- a/src/plugins/platforms/windows/qwindowsinputcontext.cpp
+++ b/src/plugins/platforms/windows/qwindowsinputcontext.cpp
@@ -717,7 +717,7 @@ int QWindowsInputContext::reconvertString(RECONVERTSTRING *reconv)
reconv->dwCompStrOffset = DWORD(startPos) * sizeof(ushort); // byte count.
reconv->dwTargetStrLen = reconv->dwCompStrLen;
reconv->dwTargetStrOffset = reconv->dwCompStrOffset;
- ushort *pastReconv = reinterpret_cast<ushort *>(reconv + 1);
+ auto *pastReconv = reinterpret_cast<ushort *>(reconv + 1);
std::copy(surroundingText.utf16(), surroundingText.utf16() + surroundingText.size(),
QT_MAKE_UNCHECKED_ARRAY_ITERATOR(pastReconv));
return memSize;
diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp
index f0754e3e57..eccf1c4928 100644
--- a/src/plugins/platforms/windows/qwindowsintegration.cpp
+++ b/src/plugins/platforms/windows/qwindowsintegration.cpp
@@ -324,7 +324,7 @@ bool QWindowsIntegration::hasCapability(QPlatformIntegration::Capability cap) co
QPlatformWindow *QWindowsIntegration::createPlatformWindow(QWindow *window) const
{
if (window->type() == Qt::Desktop) {
- QWindowsDesktopWindow *result = new QWindowsDesktopWindow(window);
+ auto *result = new QWindowsDesktopWindow(window);
qCDebug(lcQpaWindows) << "Desktop window:" << window
<< Qt::showbase << Qt::hex << result->winId() << Qt::noshowbase << Qt::dec << result->geometry();
return result;
@@ -371,7 +371,7 @@ QPlatformWindow *QWindowsIntegration::createForeignWindow(QWindow *window, WId n
qWarning("Windows QPA: Invalid foreign window ID %p.", hwnd);
return nullptr;
}
- QWindowsForeignWindow *result = new QWindowsForeignWindow(window, hwnd);
+ auto *result = new QWindowsForeignWindow(window, hwnd);
const QRect obtainedGeometry = result->geometry();
QScreen *screen = nullptr;
if (const QPlatformScreen *pScreen = result->screenForGeometry(obtainedGeometry))
diff --git a/src/plugins/platforms/windows/qwindowskeymapper.cpp b/src/plugins/platforms/windows/qwindowskeymapper.cpp
index 44668cde78..4b54051e0c 100644
--- a/src/plugins/platforms/windows/qwindowskeymapper.cpp
+++ b/src/plugins/platforms/windows/qwindowskeymapper.cpp
@@ -100,7 +100,7 @@ QWindowsKeyMapper::QWindowsKeyMapper()
: m_useRTLExtensions(false), m_keyGrabber(nullptr)
{
memset(keyLayout, 0, sizeof(keyLayout));
- QGuiApplication *app = static_cast<QGuiApplication *>(QGuiApplication::instance());
+ auto *app = static_cast<QGuiApplication *>(QGuiApplication::instance());
QObject::connect(app, &QGuiApplication::applicationStateChanged,
app, clearKeyRecorderOnApplicationInActive);
changeKeyboard();
@@ -950,7 +950,7 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg,
const UINT msgType = msg.message;
const quint32 scancode = (msg.lParam >> 16) & scancodeBitmask;
- quint32 vk_key = quint32(msg.wParam);
+ auto vk_key = quint32(msg.wParam);
quint32 nModifiers = 0;
QWindow *receiver = m_keyGrabber ? m_keyGrabber : window;
@@ -1182,7 +1182,7 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg,
// results, if we map this virtual key-code directly (for eg '?' US layouts). So try
// to find the correct key using the current message parameters & keyboard state.
if (uch.isNull() && msgType == WM_IME_KEYDOWN) {
- const QWindowsInputContext *windowsInputContext =
+ const auto *windowsInputContext =
qobject_cast<const QWindowsInputContext *>(QWindowsIntegration::instance()->inputContext());
if (!(windowsInputContext && windowsInputContext->isComposing()))
vk_key = ImmGetVirtualKey(reinterpret_cast<HWND>(window->winId()));
diff --git a/src/plugins/platforms/windows/qwindowsmenu.cpp b/src/plugins/platforms/windows/qwindowsmenu.cpp
index e55e283fe1..d20edd685e 100644
--- a/src/plugins/platforms/windows/qwindowsmenu.cpp
+++ b/src/plugins/platforms/windows/qwindowsmenu.cpp
@@ -518,7 +518,7 @@ QWindowsMenu::~QWindowsMenu()
void QWindowsMenu::insertMenuItem(QPlatformMenuItem *menuItemIn, QPlatformMenuItem *before)
{
qCDebug(lcQpaMenus) << __FUNCTION__ << '(' << menuItemIn << ", before=" << before << ')' << this;
- QWindowsMenuItem *menuItem = static_cast<QWindowsMenuItem *>(menuItemIn);
+ auto *menuItem = static_cast<QWindowsMenuItem *>(menuItemIn);
const int index = insertBefore(&m_menuItems, menuItemIn, before);
const bool append = index == m_menuItems.size() - 1;
menuItem->insertIntoMenu(this, append, index);
@@ -689,7 +689,7 @@ void QWindowsPopupMenu::showPopup(const QWindow *parentWindow, const QRect &targ
const QPlatformMenuItem *item)
{
qCDebug(lcQpaMenus) << __FUNCTION__ << '>' << this << parentWindow << targetRect << item;
- const QWindowsBaseWindow *window = static_cast<const QWindowsBaseWindow *>(parentWindow->handle());
+ const auto *window = static_cast<const QWindowsBaseWindow *>(parentWindow->handle());
const QPoint globalPos = window->mapToGlobal(targetRect.topLeft());
trackPopupMenu(window->handle(), globalPos.x(), globalPos.y());
}
@@ -756,7 +756,7 @@ QWindowsMenuBar::~QWindowsMenuBar()
void QWindowsMenuBar::insertMenu(QPlatformMenu *menuIn, QPlatformMenu *before)
{
qCDebug(lcQpaMenus) << __FUNCTION__ << menuIn << "before=" << before;
- QWindowsMenu *menu = static_cast<QWindowsMenu *>(menuIn);
+ auto *menu = static_cast<QWindowsMenu *>(menuIn);
const int index = insertBefore(&m_menus, menuIn, before);
menu->insertIntoMenuBar(this, index == m_menus.size() - 1, index);
}
diff --git a/src/plugins/platforms/windows/qwindowsmime.cpp b/src/plugins/platforms/windows/qwindowsmime.cpp
index 030d8d1e0f..b9d8b191f5 100644
--- a/src/plugins/platforms/windows/qwindowsmime.cpp
+++ b/src/plugins/platforms/windows/qwindowsmime.cpp
@@ -178,7 +178,7 @@ static bool qt_write_dibv5(QDataStream &s, QImage image)
if (image.format() != QImage::Format_ARGB32)
image = image.convertToFormat(QImage::Format_ARGB32);
- uchar *buf = new uchar[bpl_bmp];
+ auto *buf = new uchar[bpl_bmp];
memset(buf, 0, size_t(bpl_bmp));
for (int y=image.height()-1; y>=0; y--) {
@@ -264,7 +264,7 @@ static bool qt_read_dibv5(QDataStream &s, QImage &image)
const int bpl = image.bytesPerLine();
uchar *data = image.bits();
- uchar *buf24 = new uchar[bpl];
+ auto *buf24 = new uchar[bpl];
const int bpl24 = ((w * nbits + 31) / 32) * 4;
while (--h >= 0) {
@@ -286,7 +286,7 @@ static bool qt_read_dibv5(QDataStream &s, QImage &image)
if (bi.bV5Height < 0) {
// Flip the image
- uchar *buf = new uchar[bpl];
+ auto *buf = new uchar[bpl];
h = -bi.bV5Height;
for (int y = 0; y < h/2; ++y) {
memcpy(buf, data + y * bpl, size_t(bpl));
@@ -772,16 +772,16 @@ bool QWindowsMimeURI::convertFromMime(const FORMATETC &formatetc, const QMimeDat
}
QByteArray result(size, '\0');
- DROPFILES* d = reinterpret_cast<DROPFILES *>(result.data());
+ auto* d = reinterpret_cast<DROPFILES *>(result.data());
d->pFiles = sizeof(DROPFILES);
GetCursorPos(&d->pt); // try
d->fNC = true;
char *files = (reinterpret_cast<char*>(d)) + d->pFiles;
d->fWide = true;
- wchar_t *f = reinterpret_cast<wchar_t *>(files);
+ auto *f = reinterpret_cast<wchar_t *>(files);
for (int i=0; i<fileNames.size(); i++) {
- const size_t l = size_t(fileNames.at(i).length());
+ const auto l = size_t(fileNames.at(i).length());
memcpy(f, fileNames.at(i).utf16(), l * sizeof(ushort));
f += l;
*f++ = 0;
@@ -852,9 +852,9 @@ QVariant QWindowsMimeURI::convertToMime(const QString &mimeType, LPDATAOBJECT pD
if (data.isEmpty())
return QVariant();
- const DROPFILES *hdrop = reinterpret_cast<const DROPFILES *>(data.constData());
+ const auto *hdrop = reinterpret_cast<const DROPFILES *>(data.constData());
if (hdrop->fWide) {
- const wchar_t *filesw = reinterpret_cast<const wchar_t *>(data.constData() + hdrop->pFiles);
+ const auto *filesw = reinterpret_cast<const wchar_t *>(data.constData() + hdrop->pFiles);
int i = 0;
while (filesw[i]) {
QString fileurl = QString::fromWCharArray(filesw + i);
@@ -1055,7 +1055,7 @@ QVector<FORMATETC> QWindowsMimeImage::formatsForMime(const QString &mimeType, co
QVector<FORMATETC> formatetcs;
if (mimeData->hasImage() && mimeType == QLatin1String("application/x-qt-image")) {
//add DIBV5 if image has alpha channel. Do not add CF_PNG here as it will confuse MS Office (QTBUG47656).
- QImage image = qvariant_cast<QImage>(mimeData->imageData());
+ auto image = qvariant_cast<QImage>(mimeData->imageData());
if (!image.isNull() && image.hasAlphaChannel())
formatetcs += setCf(CF_DIBV5);
formatetcs += setCf(CF_DIB);
@@ -1097,7 +1097,7 @@ bool QWindowsMimeImage::convertFromMime(const FORMATETC &formatetc, const QMimeD
{
int cf = getCf(formatetc);
if ((cf == CF_DIB || cf == CF_DIBV5 || cf == int(CF_PNG)) && mimeData->hasImage()) {
- QImage img = qvariant_cast<QImage>(mimeData->imageData());
+ auto img = qvariant_cast<QImage>(mimeData->imageData());
if (img.isNull())
return false;
QByteArray ba;
diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.cpp b/src/plugins/platforms/windows/qwindowsmousehandler.cpp
index c15819a65f..6df5e6aa27 100644
--- a/src/plugins/platforms/windows/qwindowsmousehandler.cpp
+++ b/src/plugins/platforms/windows/qwindowsmousehandler.cpp
@@ -127,7 +127,7 @@ static inline QTouchDevice *createTouchDevice()
qCDebug(lcQpaEvents) << "Digitizers:" << Qt::hex << Qt::showbase << (digitizers & ~NID_READY)
<< "Ready:" << (digitizers & NID_READY) << Qt::dec << Qt::noshowbase
<< "Tablet PC:" << tabletPc << "Max touch points:" << maxTouchPoints;
- QTouchDevice *result = new QTouchDevice;
+ auto *result = new QTouchDevice;
result->setType(digitizers & NID_INTEGRATED_TOUCH
? QTouchDevice::TouchScreen : QTouchDevice::TouchPad);
QTouchDevice::Capabilities capabilities = QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::NormalizedPosition;
@@ -306,7 +306,7 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd,
// However, when tablet support is active, extraInfo is a packet serial number. This is not a problem
// since we do not want to ignore mouse events coming from a tablet.
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms703320.aspx
- const quint64 extraInfo = quint64(GetMessageExtraInfo());
+ const auto extraInfo = quint64(GetMessageExtraInfo());
if ((extraInfo & signatureMask) == miWpSignature) {
if (extraInfo & 0x80) { // Bit 7 indicates touch event, else tablet pen.
source = Qt::MouseEventSynthesizedBySystem;
@@ -370,7 +370,7 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd,
return true;
}
- QWindowsWindow *platformWindow = static_cast<QWindowsWindow *>(window->handle());
+ auto *platformWindow = static_cast<QWindowsWindow *>(window->handle());
// If the window was recently resized via mouse doubleclick on the frame or title bar,
// we don't get WM_LBUTTONDOWN or WM_LBUTTONDBLCLK for the second click,
diff --git a/src/plugins/platforms/windows/qwindowsnativeinterface.cpp b/src/plugins/platforms/windows/qwindowsnativeinterface.cpp
index e581b30ced..d1d181d66e 100644
--- a/src/plugins/platforms/windows/qwindowsnativeinterface.cpp
+++ b/src/plugins/platforms/windows/qwindowsnativeinterface.cpp
@@ -98,7 +98,7 @@ void *QWindowsNativeInterface::nativeResourceForWindow(const QByteArray &resourc
qWarning("%s: '%s' requested for null window or window without handle.", __FUNCTION__, resource.constData());
return nullptr;
}
- QWindowsWindow *bw = static_cast<QWindowsWindow *>(window->handle());
+ auto *bw = static_cast<QWindowsWindow *>(window->handle());
int type = resourceType(resource);
if (type == HandleType)
return bw->handle();
@@ -131,7 +131,7 @@ void *QWindowsNativeInterface::nativeResourceForScreen(const QByteArray &resourc
qWarning("%s: '%s' requested for null screen or screen without handle.", __FUNCTION__, resource.constData());
return nullptr;
}
- QWindowsScreen *bs = static_cast<QWindowsScreen *>(screen->handle());
+ auto *bs = static_cast<QWindowsScreen *>(screen->handle());
int type = resourceType(resource);
if (type == HandleType)
return bs->handle();
@@ -157,7 +157,7 @@ static const char customMarginPropertyC[] = "WindowsCustomMargins";
QVariant QWindowsNativeInterface::windowProperty(QPlatformWindow *window, const QString &name) const
{
- QWindowsWindow *platformWindow = static_cast<QWindowsWindow *>(window);
+ auto *platformWindow = static_cast<QWindowsWindow *>(window);
if (name == QLatin1String(customMarginPropertyC))
return QVariant::fromValue(platformWindow->customMargins());
return QVariant();
@@ -171,7 +171,7 @@ QVariant QWindowsNativeInterface::windowProperty(QPlatformWindow *window, const
void QWindowsNativeInterface::setWindowProperty(QPlatformWindow *window, const QString &name, const QVariant &value)
{
- QWindowsWindow *platformWindow = static_cast<QWindowsWindow *>(window);
+ auto *platformWindow = static_cast<QWindowsWindow *>(window);
if (name == QLatin1String(customMarginPropertyC))
platformWindow->setCustomMargins(qvariant_cast<QMargins>(value));
}
@@ -206,7 +206,7 @@ void *QWindowsNativeInterface::nativeResourceForContext(const QByteArray &resour
return nullptr;
}
- QWindowsOpenGLContext *glcontext = static_cast<QWindowsOpenGLContext *>(context->handle());
+ auto *glcontext = static_cast<QWindowsOpenGLContext *>(context->handle());
switch (resourceType(resource)) {
case RenderingContextType: // Fall through.
case EglContextType:
diff --git a/src/plugins/platforms/windows/qwindowsole.cpp b/src/plugins/platforms/windows/qwindowsole.cpp
index fb6a74581a..f3450e2806 100644
--- a/src/plugins/platforms/windows/qwindowsole.cpp
+++ b/src/plugins/platforms/windows/qwindowsole.cpp
@@ -155,7 +155,7 @@ QWindowsOleDataObject::SetData(LPFORMATETC pFormatetc, STGMEDIUM *pMedium, BOOL
HRESULT hr = ResultFromScode(E_NOTIMPL);
if (pFormatetc->cfFormat == CF_PERFORMEDDROPEFFECT && pMedium->tymed == TYMED_HGLOBAL) {
- DWORD * val = (DWORD*)GlobalLock(pMedium->hGlobal);
+ auto * val = (DWORD*)GlobalLock(pMedium->hGlobal);
performedEffect = *val;
GlobalUnlock(pMedium->hGlobal);
if (fRelease)
@@ -193,7 +193,7 @@ QWindowsOleDataObject::EnumFormatEtc(DWORD dwDirection, LPENUMFORMATETC FAR* ppe
fmtetcs.append(formatetc);
}
- QWindowsOleEnumFmtEtc *enumFmtEtc = new QWindowsOleEnumFmtEtc(fmtetcs);
+ auto *enumFmtEtc = new QWindowsOleEnumFmtEtc(fmtetcs);
*ppenumFormatEtc = enumFmtEtc;
if (enumFmtEtc->isNull()) {
delete enumFmtEtc;
@@ -237,7 +237,7 @@ QWindowsOleEnumFmtEtc::QWindowsOleEnumFmtEtc(const QVector<FORMATETC> &fmtetcs)
qCDebug(lcQpaMime) << __FUNCTION__ << fmtetcs;
m_lpfmtetcs.reserve(fmtetcs.count());
for (int idx = 0; idx < fmtetcs.count(); ++idx) {
- LPFORMATETC destetc = new FORMATETC();
+ auto destetc = new FORMATETC();
if (copyFormatEtc(destetc, &(fmtetcs.at(idx)))) {
m_lpfmtetcs.append(destetc);
} else {
@@ -255,7 +255,7 @@ QWindowsOleEnumFmtEtc::QWindowsOleEnumFmtEtc(const QVector<LPFORMATETC> &lpfmtet
m_lpfmtetcs.reserve(lpfmtetcs.count());
for (int idx = 0; idx < lpfmtetcs.count(); ++idx) {
LPFORMATETC srcetc = lpfmtetcs.at(idx);
- LPFORMATETC destetc = new FORMATETC();
+ auto destetc = new FORMATETC();
if (copyFormatEtc(destetc, srcetc)) {
m_lpfmtetcs.append(destetc);
} else {
@@ -357,7 +357,7 @@ QWindowsOleEnumFmtEtc::Clone(LPENUMFORMATETC FAR* newEnum)
if (newEnum == nullptr)
return ResultFromScode(E_INVALIDARG);
- QWindowsOleEnumFmtEtc *result = new QWindowsOleEnumFmtEtc(m_lpfmtetcs);
+ auto *result = new QWindowsOleEnumFmtEtc(m_lpfmtetcs);
result->m_nIndex = m_nIndex;
if (result->isNull()) {
diff --git a/src/plugins/platforms/windows/qwindowsopengltester.cpp b/src/plugins/platforms/windows/qwindowsopengltester.cpp
index ff495c8290..afc1991e2c 100644
--- a/src/plugins/platforms/windows/qwindowsopengltester.cpp
+++ b/src/plugins/platforms/windows/qwindowsopengltester.cpp
@@ -457,7 +457,7 @@ bool QWindowsOpenGLTester::testDesktopGL()
// Check the version. If we got 1.x then it's all hopeless and we can stop right here.
typedef const GLubyte * (APIENTRY * GetString_t)(GLenum name);
- GetString_t GetString = reinterpret_cast<GetString_t>(
+ auto GetString = reinterpret_cast<GetString_t>(
reinterpret_cast<QFunctionPointer>(::GetProcAddress(lib, "glGetString")));
if (GetString) {
if (const char *versionStr = reinterpret_cast<const char *>(GetString(GL_VERSION))) {
diff --git a/src/plugins/platforms/windows/qwindowspointerhandler.cpp b/src/plugins/platforms/windows/qwindowspointerhandler.cpp
index 36c614af34..b3d961db72 100644
--- a/src/plugins/platforms/windows/qwindowspointerhandler.cpp
+++ b/src/plugins/platforms/windows/qwindowspointerhandler.cpp
@@ -269,7 +269,10 @@ static Qt::MouseButtons queryMouseButtons()
static QWindow *getWindowUnderPointer(QWindow *window, QPoint globalPos)
{
- QWindow *currentWindowUnderPointer = QWindowsScreen::windowAt(globalPos, CWP_SKIPINVISIBLE | CWP_SKIPTRANSPARENT);
+ QWindowsWindow *platformWindow = static_cast<QWindowsWindow *>(window->handle());
+
+ QWindow *currentWindowUnderPointer = platformWindow->hasMouseCapture() ?
+ QWindowsScreen::windowAt(globalPos, CWP_SKIPINVISIBLE | CWP_SKIPTRANSPARENT) : window;
while (currentWindowUnderPointer && currentWindowUnderPointer->flags() & Qt::WindowTransparentForInput)
currentWindowUnderPointer = currentWindowUnderPointer->parent();
@@ -318,7 +321,7 @@ static QTouchDevice *createTouchDevice()
qCDebug(lcQpaEvents) << "Digitizers:" << Qt::hex << Qt::showbase << (digitizers & ~NID_READY)
<< "Ready:" << (digitizers & NID_READY) << Qt::dec << Qt::noshowbase
<< "Tablet PC:" << tabletPc << "Max touch points:" << maxTouchPoints;
- QTouchDevice *result = new QTouchDevice;
+ auto *result = new QTouchDevice;
result->setType(digitizers & NID_INTEGRATED_TOUCH
? QTouchDevice::TouchScreen : QTouchDevice::TouchPad);
QTouchDevice::Capabilities capabilities = QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::NormalizedPosition;
@@ -348,7 +351,7 @@ void QWindowsPointerHandler::handleCaptureRelease(QWindow *window,
QEvent::Type eventType,
Qt::MouseButtons mouseButtons)
{
- QWindowsWindow *platformWindow = static_cast<QWindowsWindow *>(window->handle());
+ auto *platformWindow = static_cast<QWindowsWindow *>(window->handle());
// Qt expects the platform plugin to capture the mouse on any button press until release.
if (!platformWindow->hasMouseCapture() && eventType == QEvent::MouseButtonPress) {
@@ -384,7 +387,7 @@ void QWindowsPointerHandler::handleEnterLeave(QWindow *window,
QWindow *currentWindowUnderPointer,
QPoint globalPos)
{
- QWindowsWindow *platformWindow = static_cast<QWindowsWindow *>(window->handle());
+ auto *platformWindow = static_cast<QWindowsWindow *>(window->handle());
const bool hasCapture = platformWindow->hasMouseCapture();
// No enter or leave events are sent as long as there is an autocapturing window.
@@ -468,7 +471,7 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
if (!screen)
return false;
- POINTER_TOUCH_INFO *touchInfo = static_cast<POINTER_TOUCH_INFO *>(vTouchInfo);
+ auto *touchInfo = static_cast<POINTER_TOUCH_INFO *>(vTouchInfo);
const QRect screenGeometry = screen->geometry();
@@ -548,13 +551,13 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin
if (et & QtWindows::NonClientEventFlag)
return false; // Let DefWindowProc() handle Non Client messages.
- POINTER_PEN_INFO *penInfo = static_cast<POINTER_PEN_INFO *>(vPenInfo);
+ auto *penInfo = static_cast<POINTER_PEN_INFO *>(vPenInfo);
RECT pRect, dRect;
if (!QWindowsContext::user32dll.getPointerDeviceRects(penInfo->pointerInfo.sourceDevice, &pRect, &dRect))
return false;
- const qint64 sourceDevice = (qint64)penInfo->pointerInfo.sourceDevice;
+ const auto sourceDevice = (qint64)penInfo->pointerInfo.sourceDevice;
const QPoint globalPos = QPoint(penInfo->pointerInfo.ptPixelLocation.x, penInfo->pointerInfo.ptPixelLocation.y);
const QPoint localPos = QWindowsGeometryHint::mapFromGlobal(hwnd, globalPos);
const QPointF hiResGlobalPos = QPointF(dRect.left + qreal(penInfo->pointerInfo.ptHimetricLocation.x - pRect.left)
diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp
index e3931b3bb1..d919d97211 100644
--- a/src/plugins/platforms/windows/qwindowsscreen.cpp
+++ b/src/plugins/platforms/windows/qwindowsscreen.cpp
@@ -91,7 +91,7 @@ static bool monitorData(HMONITOR hMonitor, QWindowsScreenData *data)
} else {
if (const HDC hdc = CreateDC(info.szDevice, nullptr, nullptr, nullptr)) {
const QDpi dpi = monitorDPI(hMonitor);
- data->dpi = dpi.first ? dpi : deviceDPI(hdc);
+ data->dpi = dpi.first > 0 ? dpi : deviceDPI(hdc);
data->depth = GetDeviceCaps(hdc, BITSPIXEL);
data->format = data->depth == 16 ? QImage::Format_RGB16 : QImage::Format_RGB32;
data->physicalSizeMM = QSizeF(GetDeviceCaps(hdc, HORZSIZE), GetDeviceCaps(hdc, VERTSIZE));
@@ -120,7 +120,7 @@ BOOL QT_WIN_CALLBACK monitorEnumCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM
{
QWindowsScreenData data;
if (monitorData(hMonitor, &data)) {
- WindowsScreenDataList *result = reinterpret_cast<WindowsScreenDataList *>(p);
+ auto *result = reinterpret_cast<WindowsScreenDataList *>(p);
// QWindowSystemInterface::handleScreenAdded() documentation specifies that first
// added screen will be the primary screen, so order accordingly.
// Note that the side effect of this policy is that there is no way to change primary
@@ -552,7 +552,7 @@ bool QWindowsScreenManager::handleScreenChanges()
if (existingIndex != -1) {
m_screens.at(existingIndex)->handleChanges(newData);
} else {
- QWindowsScreen *newScreen = new QWindowsScreen(newData);
+ auto *newScreen = new QWindowsScreen(newData);
m_screens.push_back(newScreen);
QWindowSystemInterface::handleScreenAdded(newScreen,
newData.flags & QWindowsScreenData::PrimaryScreen);
diff --git a/src/plugins/platforms/windows/qwindowsservices.cpp b/src/plugins/platforms/windows/qwindowsservices.cpp
index 9504513a5e..b2b1dee232 100644
--- a/src/plugins/platforms/windows/qwindowsservices.cpp
+++ b/src/plugins/platforms/windows/qwindowsservices.cpp
@@ -57,7 +57,7 @@ static inline bool shellExecute(const QUrl &url)
const QString nativeFilePath = url.isLocalFile() && !url.hasFragment() && !url.hasQuery()
? QDir::toNativeSeparators(url.toLocalFile())
: url.toString(QUrl::FullyEncoded);
- const quintptr result =
+ const auto result =
reinterpret_cast<quintptr>(ShellExecute(nullptr, nullptr,
reinterpret_cast<const wchar_t *>(nativeFilePath.utf16()),
nullptr, nullptr, SW_SHOWNORMAL));
diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp
index b75c64c40e..437c9562ab 100644
--- a/src/plugins/platforms/windows/qwindowstheme.cpp
+++ b/src/plugins/platforms/windows/qwindowstheme.cpp
@@ -162,8 +162,8 @@ public:
m_init = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
QMutexLocker readyLocker(&m_readyMutex);
- while (!m_cancelled.load()) {
- if (!m_params && !m_cancelled.load()
+ while (!m_cancelled.loadRelaxed()) {
+ if (!m_params && !m_cancelled.loadRelaxed()
&& !m_readyCondition.wait(&m_readyMutex, 1000))
continue;
@@ -174,7 +174,7 @@ public:
m_params->attributes, &info, sizeof(SHFILEINFO),
m_params->flags);
m_doneMutex.lock();
- if (!m_cancelled.load()) {
+ if (!m_cancelled.loadRelaxed()) {
*m_params->result = result;
memcpy(m_params->info, &info, sizeof(SHFILEINFO));
}
@@ -204,7 +204,7 @@ public:
void cancel()
{
QMutexLocker doneLocker(&m_doneMutex);
- m_cancelled.store(1);
+ m_cancelled.storeRelaxed(1);
m_readyCondition.wakeAll();
}
@@ -583,7 +583,7 @@ Q_GUI_EXPORT QPixmap qt_pixmapFromWinHICON(HICON icon);
static QPixmap loadIconFromShell32(int resourceId, QSizeF size)
{
if (const HMODULE hmod = QSystemLibrary::load(L"shell32")) {
- HICON iconHandle =
+ auto iconHandle =
static_cast<HICON>(LoadImage(hmod, MAKEINTRESOURCE(resourceId),
IMAGE_ICON, int(size.width()), int(size.height()), 0));
if (iconHandle) {
diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp
index b52a82c9f1..e80eefc34d 100644
--- a/src/plugins/platforms/windows/qwindowswindow.cpp
+++ b/src/plugins/platforms/windows/qwindowswindow.cpp
@@ -764,8 +764,8 @@ QWindowsWindowData
if (title.isEmpty() && (result.flags & Qt::WindowTitleHint))
title = topLevel ? qAppName() : w->objectName();
- const wchar_t *titleUtf16 = reinterpret_cast<const wchar_t *>(title.utf16());
- const wchar_t *classNameUtf16 = reinterpret_cast<const wchar_t *>(windowClassName.utf16());
+ const auto *titleUtf16 = reinterpret_cast<const wchar_t *>(title.utf16());
+ const auto *classNameUtf16 = reinterpret_cast<const wchar_t *>(windowClassName.utf16());
// Capture events before CreateWindowEx() returns. The context is cleared in
// the QWindowsWindow constructor.
@@ -883,10 +883,12 @@ static QSize toNativeSizeConstrained(QSize dip, const QWindow *w)
{
if (QHighDpiScaling::isActive()) {
const qreal factor = QHighDpiScaling::factor(w);
- if (dip.width() > 0 && dip.width() < QWINDOWSIZE_MAX)
- dip.rwidth() *= factor;
- if (dip.height() > 0 && dip.height() < QWINDOWSIZE_MAX)
- dip.rheight() *= factor;
+ if (!qFuzzyCompare(factor, qreal(1))) {
+ if (dip.width() > 0 && dip.width() < QWINDOWSIZE_MAX)
+ dip.setWidth(qRound(qreal(dip.width()) * factor));
+ if (dip.height() > 0 && dip.height() < QWINDOWSIZE_MAX)
+ dip.setHeight(qRound(qreal(dip.height()) * factor));
+ }
}
return dip;
}
@@ -980,7 +982,7 @@ bool QWindowsGeometryHint::handleCalculateSize(const QMargins &customMargins, co
if (!msg.wParam || customMargins.isNull())
return false;
*result = DefWindowProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
- NCCALCSIZE_PARAMS *ncp = reinterpret_cast<NCCALCSIZE_PARAMS *>(msg.lParam);
+ auto *ncp = reinterpret_cast<NCCALCSIZE_PARAMS *>(msg.lParam);
const RECT oldClientArea = ncp->rgrc[0];
ncp->rgrc[0].left += customMargins.left();
ncp->rgrc[0].top += customMargins.top();
@@ -1500,7 +1502,7 @@ QWindow *QWindowsWindow::topLevelOf(QWindow *w)
w = parent;
if (const QPlatformWindow *handle = w->handle()) {
- const QWindowsWindow *ww = static_cast<const QWindowsWindow *>(handle);
+ const auto *ww = static_cast<const QWindowsWindow *>(handle);
if (ww->isEmbedded()) {
HWND parentHWND = GetAncestor(ww->handle(), GA_PARENT);
const HWND desktopHwnd = GetDesktopWindow();
@@ -1574,7 +1576,7 @@ bool QWindowsWindow::isActive() const
bool QWindowsWindow::isAncestorOf(const QPlatformWindow *child) const
{
- const QWindowsWindow *childWindow = static_cast<const QWindowsWindow *>(child);
+ const auto *childWindow = static_cast<const QWindowsWindow *>(child);
return IsChild(m_data.hwnd, childWindow->handle());
}
@@ -1723,7 +1725,7 @@ void QWindowsWindow::setParent_sys(const QPlatformWindow *parent)
HWND oldParentHWND = parentHwnd();
HWND newParentHWND = nullptr;
if (parent) {
- const QWindowsWindow *parentW = static_cast<const QWindowsWindow *>(parent);
+ const auto *parentW = static_cast<const QWindowsWindow *>(parent);
newParentHWND = parentW->handle();
}
@@ -2366,7 +2368,7 @@ void QWindowsWindow::propagateSizeHints()
bool QWindowsWindow::handleGeometryChangingMessage(MSG *message, const QWindow *qWindow, const QMargins &margins)
{
- WINDOWPOS *windowPos = reinterpret_cast<WINDOWPOS *>(message->lParam);
+ auto *windowPos = reinterpret_cast<WINDOWPOS *>(message->lParam);
if ((windowPos->flags & SWP_NOZORDER) == 0) {
if (QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(qWindow)) {
QWindow *parentWindow = qWindow->parent();
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
index a427e553f0..b276798316 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
@@ -82,7 +82,7 @@ QWindowsUiaMainProvider *QWindowsUiaMainProvider::providerForAccessible(QAccessi
QAccessible::Id id = QAccessible::uniqueId(accessible);
QWindowsUiaProviderCache *providerCache = QWindowsUiaProviderCache::instance();
- QWindowsUiaMainProvider *provider = qobject_cast<QWindowsUiaMainProvider *>(providerCache->providerForId(id));
+ auto *provider = qobject_cast<QWindowsUiaMainProvider *>(providerCache->providerForId(id));
if (provider) {
provider->AddRef();
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.cpp
index 9d1e72fb78..50ecfc7ecd 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.cpp
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextprovider.cpp
@@ -100,7 +100,7 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaTextProvider::GetSelection(SAFEARRAY **pRet
for (LONG i = 0; i < selCount; ++i) {
int startOffset = 0, endOffset = 0;
textInterface->selection((int)i, &startOffset, &endOffset);
- QWindowsUiaTextRangeProvider *textRangeProvider = new QWindowsUiaTextRangeProvider(id(), startOffset, endOffset);
+ auto *textRangeProvider = new QWindowsUiaTextRangeProvider(id(), startOffset, endOffset);
SafeArrayPutElement(*pRetVal, &i, static_cast<IUnknown *>(textRangeProvider));
textRangeProvider->Release();
}
@@ -110,7 +110,7 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaTextProvider::GetSelection(SAFEARRAY **pRet
if ((*pRetVal = SafeArrayCreateVector(VT_UNKNOWN, 0, 1))) {
LONG i = 0;
int cursorPosition = textInterface->cursorPosition();
- QWindowsUiaTextRangeProvider *textRangeProvider = new QWindowsUiaTextRangeProvider(id(), cursorPosition, cursorPosition);
+ auto *textRangeProvider = new QWindowsUiaTextRangeProvider(id(), cursorPosition, cursorPosition);
SafeArrayPutElement(*pRetVal, &i, static_cast<IUnknown *>(textRangeProvider));
textRangeProvider->Release();
}
@@ -138,7 +138,7 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaTextProvider::GetVisibleRanges(SAFEARRAY **
// Considering the entire text as visible.
if ((*pRetVal = SafeArrayCreateVector(VT_UNKNOWN, 0, 1))) {
LONG i = 0;
- QWindowsUiaTextRangeProvider *textRangeProvider = new QWindowsUiaTextRangeProvider(id(), 0, textInterface->characterCount());
+ auto *textRangeProvider = new QWindowsUiaTextRangeProvider(id(), 0, textInterface->characterCount());
SafeArrayPutElement(*pRetVal, &i, static_cast<IUnknown *>(textRangeProvider));
textRangeProvider->Release();
}
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.cpp
index 1be186f6b3..8e395669f8 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.cpp
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiatextrangeprovider.cpp
@@ -92,7 +92,7 @@ HRESULT QWindowsUiaTextRangeProvider::Compare(ITextRangeProvider *range, BOOL *p
if (!range || !pRetVal)
return E_INVALIDARG;
- QWindowsUiaTextRangeProvider *targetProvider = static_cast<QWindowsUiaTextRangeProvider *>(range);
+ auto *targetProvider = static_cast<QWindowsUiaTextRangeProvider *>(range);
*pRetVal = ((targetProvider->m_startOffset == m_startOffset) && (targetProvider->m_endOffset == m_endOffset));
return S_OK;
}
@@ -110,7 +110,7 @@ HRESULT QWindowsUiaTextRangeProvider::CompareEndpoints(TextPatternRangeEndpoint
if (!targetRange || !pRetVal)
return E_INVALIDARG;
- QWindowsUiaTextRangeProvider *targetProvider = static_cast<QWindowsUiaTextRangeProvider *>(targetRange);
+ auto *targetProvider = static_cast<QWindowsUiaTextRangeProvider *>(targetRange);
int point = (endpoint == TextPatternRangeEndpoint_Start) ? m_startOffset : m_endOffset;
int targetPoint = (targetEndpoint == TextPatternRangeEndpoint_Start) ?
@@ -373,7 +373,7 @@ HRESULT QWindowsUiaTextRangeProvider::MoveEndpointByRange(TextPatternRangeEndpoi
qCDebug(lcQpaUiAutomation) << __FUNCTION__
<< "endpoint=" << endpoint << "targetRange=" << targetRange << "targetEndpoint=" << targetEndpoint << "this: " << this;
- QWindowsUiaTextRangeProvider *targetProvider = static_cast<QWindowsUiaTextRangeProvider *>(targetRange);
+ auto *targetProvider = static_cast<QWindowsUiaTextRangeProvider *>(targetRange);
int targetPoint = (targetEndpoint == TextPatternRangeEndpoint_Start) ?
targetProvider->m_startOffset : targetProvider->m_endOffset;
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp
index 7980826b49..ab04384616 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp
@@ -114,7 +114,7 @@ void setVariantBool(bool value, VARIANT *variant)
void setVariantDouble(double value, VARIANT *variant)
{
variant->vt = VT_R8;
- variant->boolVal = value;
+ variant->dblVal = value;
}
BSTR bStrFromQString(const QString &value)
diff --git a/src/plugins/platforms/winrt/qwinrtscreen.cpp b/src/plugins/platforms/winrt/qwinrtscreen.cpp
index e611c7be24..0d77889a36 100644
--- a/src/plugins/platforms/winrt/qwinrtscreen.cpp
+++ b/src/plugins/platforms/winrt/qwinrtscreen.cpp
@@ -1097,7 +1097,7 @@ HRESULT QWinRTScreen::onPointerEntered(ICoreWindow *, IPointerEventArgs *args)
d->currentTargetWindow = topWindow();
if (d->mouseGrabWindow)
- d->currentTargetWindow = d->mouseGrabWindow.load()->window();
+ d->currentTargetWindow = d->mouseGrabWindow.loadRelaxed()->window();
qCDebug(lcQpaEvents) << __FUNCTION__ << "handleEnterEvent" << d->currentTargetWindow << pos;
QWindowSystemInterface::handleEnterEvent(d->currentTargetWindow, pos, pos);
@@ -1121,7 +1121,7 @@ HRESULT QWinRTScreen::onPointerExited(ICoreWindow *, IPointerEventArgs *args)
d->touchPoints.remove(id);
if (d->mouseGrabWindow)
- d->currentTargetWindow = d->mouseGrabWindow.load()->window();
+ d->currentTargetWindow = d->mouseGrabWindow.loadRelaxed()->window();
qCDebug(lcQpaEvents) << __FUNCTION__ << "handleLeaveEvent" << d->currentTargetWindow;
QWindowSystemInterface::handleLeaveEvent(d->currentTargetWindow);
@@ -1152,7 +1152,7 @@ HRESULT QWinRTScreen::onPointerUpdated(ICoreWindow *, IPointerEventArgs *args)
d->currentTargetWindow = windowUnderPointer;
if (d->mouseGrabWindow)
- d->currentTargetWindow = d->mouseGrabWindow.load()->window();
+ d->currentTargetWindow = d->mouseGrabWindow.loadRelaxed()->window();
if (d->currentTargetWindow) {
const QPointF globalPosDelta = pos - posPoint;
@@ -1354,7 +1354,7 @@ void QWinRTScreen::emulateMouseMove(const QPointF &point, MousePositionTransitio
d->currentTargetWindow = windowUnderPointer;
if (d->mouseGrabWindow)
- d->currentTargetWindow = d->mouseGrabWindow.load()->window();
+ d->currentTargetWindow = d->mouseGrabWindow.loadRelaxed()->window();
if (d->currentTargetWindow) {
const QPointF globalPosDelta = pos - posPoint;
diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp
index 4adf662152..f26f698e76 100644
--- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp
+++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp
@@ -204,7 +204,6 @@ QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlat
, m_shareContext(0)
, m_format(format)
, m_isPBufferCurrent(false)
- , m_swapInterval(-1)
, m_ownsContext(nativeHandle.isNull())
, m_getGraphicsResetStatus(0)
, m_lost(false)
@@ -567,9 +566,9 @@ bool QGLXContext::makeCurrent(QPlatformSurface *surface)
if (success && surfaceClass == QSurface::Window) {
int interval = surface->format().swapInterval();
+ QXcbWindow *window = static_cast<QXcbWindow *>(surface);
QXcbScreen *screen = screenForPlatformSurface(surface);
- if (interval >= 0 && m_swapInterval != interval && screen) {
- m_swapInterval = interval;
+ if (interval >= 0 && interval != window->swapInterval() && screen) {
typedef void (*qt_glXSwapIntervalEXT)(Display *, GLXDrawable, int);
typedef void (*qt_glXSwapIntervalMESA)(unsigned int);
static qt_glXSwapIntervalEXT glXSwapIntervalEXT = 0;
@@ -588,6 +587,7 @@ bool QGLXContext::makeCurrent(QPlatformSurface *surface)
glXSwapIntervalEXT(m_display, glxDrawable, interval);
else if (glXSwapIntervalMESA)
glXSwapIntervalMESA(interval);
+ window->setSwapInterval(interval);
}
}
diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.h b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.h
index be9d3f5dcb..2a88fd6e59 100644
--- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.h
+++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.h
@@ -87,7 +87,6 @@ private:
GLXContext m_shareContext;
QSurfaceFormat m_format;
bool m_isPBufferCurrent;
- int m_swapInterval;
bool m_ownsContext;
GLenum (APIENTRY * m_getGraphicsResetStatus)();
bool m_lost;
diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp
index bfc105a040..8c34bd9c91 100644
--- a/src/plugins/platforms/xcb/qxcbscreen.cpp
+++ b/src/plugins/platforms/xcb/qxcbscreen.cpp
@@ -658,16 +658,24 @@ QImage::Format QXcbScreen::format() const
return format;
}
-QDpi QXcbScreen::logicalDpi() const
+int QXcbScreen::forcedDpi() const
{
static const int overrideDpi = qEnvironmentVariableIntValue("QT_FONT_DPI");
if (overrideDpi)
- return QDpi(overrideDpi, overrideDpi);
+ return overrideDpi;
const int forcedDpi = m_virtualDesktop->forcedDpi();
- if (forcedDpi > 0) {
+ if (forcedDpi > 0)
+ return forcedDpi;
+ return 0;
+}
+
+QDpi QXcbScreen::logicalDpi() const
+{
+ const int forcedDpi = this->forcedDpi();
+ if (forcedDpi > 0)
return QDpi(forcedDpi, forcedDpi);
- }
+
return m_virtualDesktop->dpi();
}
@@ -739,7 +747,9 @@ void QXcbScreen::updateGeometry(const QRect &geometry, uint8_t rotation)
if (m_sizeMillimeters.isEmpty())
m_sizeMillimeters = sizeInMillimeters(geometry.size(), m_virtualDesktop->dpi());
- qreal dpi = geometry.width() / physicalSize().width() * qreal(25.4);
+ qreal dpi = forcedDpi();
+ if (dpi <= 0)
+ dpi = geometry.width() / physicalSize().width() * qreal(25.4);
// Use 128 as a reference DPI on small screens. This favors "small UI" over "large UI".
qreal referenceDpi = physicalSize().width() <= 320 ? 128 : 96;
diff --git a/src/plugins/platforms/xcb/qxcbscreen.h b/src/plugins/platforms/xcb/qxcbscreen.h
index be6c45e415..ec3b07bfb7 100644
--- a/src/plugins/platforms/xcb/qxcbscreen.h
+++ b/src/plugins/platforms/xcb/qxcbscreen.h
@@ -208,6 +208,7 @@ public:
private:
void sendStartupMessage(const QByteArray &message) const;
+ int forcedDpi() const;
QByteArray getOutputProperty(xcb_atom_t atom) const;
QByteArray getEdid() const;
diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h
index e4a04f5308..5de5974ca7 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.h
+++ b/src/plugins/platforms/xcb/qxcbwindow.h
@@ -184,6 +184,9 @@ public:
static void setWindowTitle(const QXcbConnection *conn, xcb_window_t window, const QString &title);
static QString windowTitle(const QXcbConnection *conn, xcb_window_t window);
+ int swapInterval() const { return m_swapInterval; }
+ void setSwapInterval(int swapInterval) { m_swapInterval = swapInterval; }
+
public Q_SLOTS:
void updateSyncRequestCounter();
@@ -276,6 +279,7 @@ protected:
SyncState m_syncState = NoSyncNeeded;
QXcbSyncWindowRequest *m_pendingSyncRequest = nullptr;
+ int m_swapInterval = -1;
};
class QXcbForeignWindow : public QXcbWindow
diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm
index 743713c86a..6c4605b5d2 100644
--- a/src/plugins/styles/mac/qmacstyle_mac.mm
+++ b/src/plugins/styles/mac/qmacstyle_mac.mm
@@ -82,53 +82,6 @@ static QWindow *qt_getWindow(const QWidget *widget)
return widget ? widget->window()->windowHandle() : 0;
}
-@interface QT_MANGLE_NAMESPACE(NotificationReceiver) : NSObject
-@end
-
-QT_NAMESPACE_ALIAS_OBJC_CLASS(NotificationReceiver);
-
-@implementation NotificationReceiver
-{
- QMacStylePrivate *privateStyle;
-}
-
-- (instancetype)initWithPrivateStyle:(QMacStylePrivate *)style
-{
- if (self = [super init])
- privateStyle = style;
- return self;
-}
-
-- (void)scrollBarStyleDidChange:(NSNotification *)notification
-{
- Q_UNUSED(notification);
-
- // purge destroyed scroll bars:
- QMacStylePrivate::scrollBars.removeAll(QPointer<QObject>());
-
- QEvent event(QEvent::StyleChange);
- for (const auto &o : QMacStylePrivate::scrollBars)
- QCoreApplication::sendEvent(o, &event);
-}
-
-- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
- change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context
-{
- Q_UNUSED(keyPath);
- Q_UNUSED(object);
- Q_UNUSED(change);
- Q_UNUSED(context);
-
- Q_ASSERT([keyPath isEqualToString:@"effectiveAppearance"]);
- Q_ASSERT(object == NSApp);
-
- for (NSView *b : privateStyle->cocoaControls)
- [b release];
- privateStyle->cocoaControls.clear();
-}
-
-@end
-
@interface QT_MANGLE_NAMESPACE(QIndeterminateProgressIndicator) : NSProgressIndicator
@property (readonly, nonatomic) NSInteger animators;
@@ -2032,33 +1985,34 @@ void QMacStylePrivate::resolveCurrentNSView(QWindow *window) const
QMacStyle::QMacStyle()
: QCommonStyle(*new QMacStylePrivate)
{
- Q_D(QMacStyle);
QMacAutoReleasePool pool;
- d->receiver = [[NotificationReceiver alloc] initWithPrivateStyle:d];
- [[NSNotificationCenter defaultCenter] addObserver:d->receiver
- selector:@selector(scrollBarStyleDidChange:)
- name:NSPreferredScrollerStyleDidChangeNotification
- object:nil];
+ static QMacNotificationObserver scrollbarStyleObserver(nil,
+ NSPreferredScrollerStyleDidChangeNotification, []() {
+ // Purge destroyed scroll bars
+ QMacStylePrivate::scrollBars.removeAll(QPointer<QObject>());
+
+ QEvent event(QEvent::StyleChange);
+ for (const auto &o : QMacStylePrivate::scrollBars)
+ QCoreApplication::sendEvent(o, &event);
+ });
+
#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14)
+ Q_D(QMacStyle);
+ // FIXME: Tie this logic into theme change, or even polish/unpolish
if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave) {
- [NSApplication.sharedApplication addObserver:d->receiver forKeyPath:@"effectiveAppearance"
- options:NSKeyValueObservingOptionNew context:nullptr];
+ d->appearanceObserver = QMacKeyValueObserver(NSApp, @"effectiveAppearance", [this] {
+ Q_D(QMacStyle);
+ for (NSView *b : d->cocoaControls)
+ [b release];
+ d->cocoaControls.clear();
+ });
}
#endif
}
QMacStyle::~QMacStyle()
{
- Q_D(QMacStyle);
- QMacAutoReleasePool pool;
-
- [[NSNotificationCenter defaultCenter] removeObserver:d->receiver];
-#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14)
- if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave)
- [NSApplication.sharedApplication removeObserver:d->receiver forKeyPath:@"effectiveAppearance"];
-#endif
- [d->receiver release];
}
void QMacStyle::polish(QPalette &)
@@ -2542,11 +2496,12 @@ int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QW
QPalette QMacStyle::standardPalette() const
{
- QPalette pal = QCommonStyle::standardPalette();
- pal.setColor(QPalette::Disabled, QPalette::Dark, QColor(191, 191, 191));
- pal.setColor(QPalette::Active, QPalette::Dark, QColor(191, 191, 191));
- pal.setColor(QPalette::Inactive, QPalette::Dark, QColor(191, 191, 191));
- return pal;
+ auto platformTheme = QGuiApplicationPrivate::platformTheme();
+ auto styleNames = platformTheme->themeHint(QPlatformTheme::StyleNames);
+ if (styleNames.toStringList().contains("macintosh"))
+ return *platformTheme->palette();
+ else
+ return QStyle::standardPalette();
}
int QMacStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w,
@@ -3094,7 +3049,9 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai
theStroker.setCapStyle(Qt::FlatCap);
theStroker.setDashPattern(QVector<qreal>() << 1 << 2);
path = theStroker.createStroke(path);
- p->fillPath(path, QColor(0, 0, 0, 119));
+ const auto dark = qt_mac_applicationIsInDarkMode() ? opt->palette.dark().color().darker()
+ : QColor(0, 0, 0, 119);
+ p->fillPath(path, dark);
}
break;
case PE_FrameWindow:
diff --git a/src/plugins/styles/mac/qmacstyle_mac_p_p.h b/src/plugins/styles/mac/qmacstyle_mac_p_p.h
index 6e7004485c..d6af18f01f 100644
--- a/src/plugins/styles/mac/qmacstyle_mac_p_p.h
+++ b/src/plugins/styles/mac/qmacstyle_mac_p_p.h
@@ -156,7 +156,6 @@ Q_FORWARD_DECLARE_MUTABLE_CG_TYPE(CGContext);
Q_FORWARD_DECLARE_OBJC_CLASS(NSView);
Q_FORWARD_DECLARE_OBJC_CLASS(NSCell);
-Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(NotificationReceiver));
QT_BEGIN_NAMESPACE
@@ -295,13 +294,14 @@ public:
static QVector<QPointer<QObject> > scrollBars;
mutable QPointer<QFocusFrame> focusWidget;
- QT_MANGLE_NAMESPACE(NotificationReceiver) *receiver;
mutable NSView *backingStoreNSView;
mutable QHash<CocoaControl, NSView *> cocoaControls;
mutable QHash<CocoaControl, NSCell *> cocoaCells;
QFont smallSystemFont;
QFont miniSystemFont;
+
+ QMacKeyValueObserver appearanceObserver;
};
QT_END_NAMESPACE
diff --git a/src/printsupport/doc/src/dontdocument.qdoc b/src/printsupport/doc/src/dontdocument.qdoc
new file mode 100644
index 0000000000..74552b4e99
--- /dev/null
+++ b/src/printsupport/doc/src/dontdocument.qdoc
@@ -0,0 +1,30 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \dontdocument (QMetaTypeId QPlatformPrintDevice QPlatformPrinterSupportPlugin)
+*/
diff --git a/src/sql/doc/src/dontdocument.qdoc b/src/sql/doc/src/dontdocument.qdoc
new file mode 100644
index 0000000000..0193ace4b4
--- /dev/null
+++ b/src/sql/doc/src/dontdocument.qdoc
@@ -0,0 +1,30 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \dontdocument (QTypeInfo)
+*/
diff --git a/src/sql/kernel/qsqldatabase.cpp b/src/sql/kernel/qsqldatabase.cpp
index d63a9e59a8..99aa3e96c4 100644
--- a/src/sql/kernel/qsqldatabase.cpp
+++ b/src/sql/kernel/qsqldatabase.cpp
@@ -184,7 +184,7 @@ QSqlDatabasePrivate *QSqlDatabasePrivate::shared_null()
void QSqlDatabasePrivate::invalidateDb(const QSqlDatabase &db, const QString &name, bool doWarn)
{
- if (db.d->ref.load() != 1 && doWarn) {
+ if (db.d->ref.loadRelaxed() != 1 && doWarn) {
qWarning("QSqlDatabasePrivate::removeDatabase: connection '%s' is still in use, "
"all queries will cease to work.", name.toLocal8Bit().constData());
db.d->disable();
diff --git a/src/sql/kernel/qsqlquery.cpp b/src/sql/kernel/qsqlquery.cpp
index daadcb8a0e..e7c444f5b9 100644
--- a/src/sql/kernel/qsqlquery.cpp
+++ b/src/sql/kernel/qsqlquery.cpp
@@ -374,7 +374,7 @@ bool QSqlQuery::exec(const QString& query)
QElapsedTimer t;
t.start();
#endif
- if (d->ref.load() != 1) {
+ if (d->ref.loadRelaxed() != 1) {
bool fo = isForwardOnly();
*this = QSqlQuery(driver()->createResult());
d->sqlResult->setNumericalPrecisionPolicy(d->sqlResult->numericalPrecisionPolicy());
@@ -960,7 +960,7 @@ void QSqlQuery::clear()
*/
bool QSqlQuery::prepare(const QString& query)
{
- if (d->ref.load() != 1) {
+ if (d->ref.loadRelaxed() != 1) {
bool fo = isForwardOnly();
*this = QSqlQuery(driver()->createResult());
setForwardOnly(fo);
diff --git a/src/src.pro b/src/src.pro
index 1c76a2e46f..b1afdd27a5 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -21,10 +21,6 @@ src_tools_rcc.subdir = tools/rcc
src_tools_rcc.target = sub-rcc
src_tools_rcc.depends = src_tools_bootstrap
-src_tools_qfloat16_tables.subdir = tools/qfloat16-tables
-src_tools_qfloat16_tables.target = sub-qfloat16-tables
-src_tools_qfloat16_tables.depends = src_tools_bootstrap
-
src_tools_qlalr.subdir = tools/qlalr
src_tools_qlalr.target = sub-qlalr
force_bootstrap: src_tools_qlalr.depends = src_tools_bootstrap
@@ -57,6 +53,10 @@ src_tools_androiddeployqt.subdir = tools/androiddeployqt
src_tools_androiddeployqt.target = sub-androiddeployqt
src_tools_androiddeployqt.depends = src_corelib
+src_tools_androidtestrunner.subdir = tools/androidtestrunner
+src_tools_androidtestrunner.target = sub-androidtestrunner
+src_tools_androidtestrunner.depends = src_corelib
+
src_tools_qvkgen.subdir = tools/qvkgen
src_tools_qvkgen.target = sub-qvkgen
force_bootstrap: src_tools_qvkgen.depends = src_tools_bootstrap
@@ -68,7 +68,7 @@ src_winmain.depends = sub-corelib # just for the module .pri file
src_corelib.subdir = $$PWD/corelib
src_corelib.target = sub-corelib
-src_corelib.depends = src_tools_moc src_tools_rcc src_tools_qfloat16_tables
+src_corelib.depends = src_tools_moc src_tools_rcc
src_xml.subdir = $$PWD/xml
src_xml.target = sub-xml
@@ -155,12 +155,12 @@ src_android.subdir = $$PWD/android
src_3rdparty_freetype.depends += src_corelib
}
}
-SUBDIRS += src_tools_bootstrap src_tools_moc src_tools_rcc src_tools_qfloat16_tables
+SUBDIRS += src_tools_bootstrap src_tools_moc src_tools_rcc
qtConfig(regularexpression):pcre2 {
SUBDIRS += src_3rdparty_pcre2
src_corelib.depends += src_3rdparty_pcre2
}
-TOOLS = src_tools_moc src_tools_rcc src_tools_qlalr src_tools_qfloat16_tables
+TOOLS = src_tools_moc src_tools_rcc src_tools_qlalr
!force_bootstrap:if(qtConfig(lttng)|qtConfig(etw)) {
SUBDIRS += src_tools_tracegen
src_corelib.depends += src_tools_tracegen
@@ -189,8 +189,10 @@ qtConfig(dbus) {
}
android {
- SUBDIRS += src_tools_androiddeployqt
- TOOLS += src_tools_androiddeployqt
+ SUBDIRS += src_tools_androiddeployqt \
+ src_tools_androidtestrunner
+ TOOLS += src_tools_androiddeployqt \
+ src_tools_androidtestrunner
}
qtConfig(concurrent): SUBDIRS += src_concurrent
diff --git a/src/testlib/doc/src/dontdocument.qdoc b/src/testlib/doc/src/dontdocument.qdoc
new file mode 100644
index 0000000000..59270c7a4f
--- /dev/null
+++ b/src/testlib/doc/src/dontdocument.qdoc
@@ -0,0 +1,32 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \dontdocument (QTestEventLoop QTestData QEventSizeOfChecker QSpontaneKeyEvent
+ QTestEvent QTestKeyEvent QTestKeyClicksEvent QTestMouseEvent
+ QTestDelayEvent QMetaTypeId)
+*/
diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp
index 5faafba38d..ac309374e3 100644
--- a/src/testlib/qtestcase.cpp
+++ b/src/testlib/qtestcase.cpp
@@ -1012,14 +1012,14 @@ public:
WatchDog()
{
QMutexLocker locker(&mutex);
- timeout.store(-1);
+ timeout.storeRelaxed(-1);
start();
waitCondition.wait(&mutex);
}
~WatchDog() {
{
QMutexLocker locker(&mutex);
- timeout.store(0);
+ timeout.storeRelaxed(0);
waitCondition.wakeAll();
}
wait();
@@ -1027,13 +1027,13 @@ public:
void beginTest() {
QMutexLocker locker(&mutex);
- timeout.store(defaultTimeout());
+ timeout.storeRelaxed(defaultTimeout());
waitCondition.wakeAll();
}
void testFinished() {
QMutexLocker locker(&mutex);
- timeout.store(-1);
+ timeout.storeRelaxed(-1);
waitCondition.wakeAll();
}
@@ -1041,7 +1041,7 @@ public:
QMutexLocker locker(&mutex);
waitCondition.wakeAll();
while (1) {
- int t = timeout.load();
+ int t = timeout.loadRelaxed();
if (!t)
break;
if (Q_UNLIKELY(!waitCondition.wait(&mutex, t))) {
diff --git a/src/testlib/qtestlog.cpp b/src/testlib/qtestlog.cpp
index faef3912c4..14543c3cde 100644
--- a/src/testlib/qtestlog.cpp
+++ b/src/testlib/qtestlog.cpp
@@ -219,7 +219,7 @@ namespace QTest {
}
if (type != QtFatalMsg) {
- if (counter.load() <= 0)
+ if (counter.loadRelaxed() <= 0)
return;
if (!counter.deref()) {
diff --git a/src/tools/androiddeployqt/main.cpp b/src/tools/androiddeployqt/main.cpp
index 65d95362f7..10bbd59bfb 100644
--- a/src/tools/androiddeployqt/main.cpp
+++ b/src/tools/androiddeployqt/main.cpp
@@ -118,7 +118,6 @@ struct Options
, timing(false)
, generateAssetsFileList(true)
, build(true)
- , gradle(false)
, auxMode(false)
, deploymentMechanism(Bundled)
, releasePackage(false)
@@ -150,7 +149,6 @@ struct Options
bool timing;
bool generateAssetsFileList;
bool build;
- bool gradle;
bool auxMode;
bool stripLibraries = true;
ActionTimer timer;
@@ -159,7 +157,6 @@ struct Options
QString sdkPath;
QString sdkBuildToolsVersion;
QString ndkPath;
- QString antTool;
QString jdkPath;
// Build paths
@@ -212,6 +209,7 @@ struct Options
bool sectionsOnly;
bool protectedAuthenticationPath;
bool jarSigner;
+ QString apkPath;
// Gdbserver
TriState gdbServer;
@@ -369,13 +367,6 @@ Options parseOptions()
options.helpRequested = true;
} else if (argument.compare(QLatin1String("--verbose"), Qt::CaseInsensitive) == 0) {
options.verbose = true;
- } else if (argument.compare(QLatin1String("--gradle"), Qt::CaseInsensitive) == 0) {
- options.gradle = true;
- } else if (argument.compare(QLatin1String("--ant"), Qt::CaseInsensitive) == 0) {
- if (i + 1 == arguments.size())
- options.helpRequested = true;
- else
- options.antTool = arguments.at(++i);
} else if (argument.compare(QLatin1String("--deployment"), Qt::CaseInsensitive) == 0) {
if (i + 1 == arguments.size()) {
options.helpRequested = true;
@@ -406,6 +397,11 @@ Options parseOptions()
options.helpRequested = true;
else
options.jdkPath = arguments.at(++i);
+ } else if (argument.compare(QLatin1String("--apk"), Qt::CaseInsensitive) == 0) {
+ if (i + 1 == arguments.size())
+ options.helpRequested = true;
+ else
+ options.apkPath = arguments.at(++i);
} else if (argument.compare(QLatin1String("--sign"), Qt::CaseInsensitive) == 0) {
if (i + 2 >= arguments.size()) {
options.helpRequested = true;
@@ -515,9 +511,6 @@ void printHelp()
" --android-platform <platform>: Builds against the given android\n"
" platform. By default, the highest available version will be\n"
" used.\n"
- " --gradle. Use gradle instead of ant to create and install the apk.\n"
- " --ant <path/to/ant>: If unspecified, ant from the PATH will be\n"
- " used.\n"
" --release: Builds a package ready for release. By default, the\n"
" package will be signed with a debug key.\n"
" --sign <url/to/keystore> <alias>: Signs the package with the\n"
@@ -557,6 +550,7 @@ void printHelp()
" dependencies into the build directory and update the XML templates.\n"
" The project will not be built or installed.\n"
" --no-strip: Do not strip debug symbols from libraries.\n"
+ " --apk <path/where/to/copy/the/apk>: Path where to copy the built apk.\n"
" --help: Displays this information.\n\n",
qPrintable(QCoreApplication::arguments().at(0))
);
@@ -1024,16 +1018,13 @@ bool copyAndroidTemplate(const Options &options)
if (options.verbose)
fprintf(stdout, "Copying Android package template.\n");
- if (options.gradle && !copyGradleTemplate(options))
+ if (!copyGradleTemplate(options))
return false;
if (!copyAndroidTemplate(options, QLatin1String("/src/android/templates")))
return false;
- if (options.gradle)
- return true;
-
- return copyAndroidTemplate(options, QLatin1String("/src/android/java"));
+ return true;
}
bool copyAndroidSources(const Options &options)
@@ -1285,36 +1276,6 @@ bool updateStringsXml(const Options &options)
if (!updateFile(fileName, replacements))
return false;
- if (options.gradle)
- return true;
-
- // ant can't (easily) build multiple res folders,
- // so we need to replace the "<!-- %%INSERT_STRINGS -->" placeholder
- // from the main res folder
- QFile stringsXml(fileName);
- if (!stringsXml.open(QIODevice::ReadOnly)) {
- fprintf(stderr, "Cannot open %s for reading.\n", qPrintable(fileName));
- return false;
- }
-
- QXmlStreamReader reader(&stringsXml);
- while (!reader.atEnd()) {
- reader.readNext();
- if (reader.isStartElement() &&
- reader.name() == QLatin1String("string") &&
- reader.attributes().hasAttribute(QLatin1String("name")) &&
- reader.attributes().value(QLatin1String("name")) == QLatin1String("app_name")) {
- return true;
- }
- }
-
- replacements.clear();
- replacements[QStringLiteral("<!-- %%INSERT_STRINGS -->")] = QString::fromLatin1("<string name=\"app_name\">%1</string>\n")
- .arg(QFileInfo(options.applicationBinary).baseName().mid(sizeof("lib") - 1));
-
- if (!updateFile(fileName, replacements))
- return false;
-
return true;
}
@@ -2220,64 +2181,6 @@ QString findInPath(const QString &fileName)
return QString();
}
-bool buildAntProject(const Options &options)
-{
- if (options.verbose)
- fprintf(stdout, "Building Android package using ant.\n");
-
- QString antTool = options.antTool;
- if (antTool.isEmpty()) {
-#if defined(Q_OS_WIN32)
- antTool = findInPath(QLatin1String("ant.bat"));
-#else
- antTool = findInPath(QLatin1String("ant"));
-#endif
- }
-
- if (antTool.isEmpty()) {
- fprintf(stderr, "Cannot find ant in PATH. Please use --ant option to pass in the correct path.\n");
- return false;
- }
-
- if (options.verbose)
- fprintf(stdout, "Using ant: %s\n", qPrintable(antTool));
-
- QString oldPath = QDir::currentPath();
- if (!QDir::setCurrent(options.outputDirectory)) {
- fprintf(stderr, "Cannot current path to %s\n", qPrintable(options.outputDirectory));
- return false;
- }
-
- QString ant = QString::fromLatin1("%1 %2").arg(shellQuote(antTool)).arg(options.releasePackage ? QLatin1String(" release") : QLatin1String(" debug"));
-
- FILE *antCommand = openProcess(ant);
- if (antCommand == 0) {
- fprintf(stderr, "Cannot run ant command: %s\n.", qPrintable(ant));
- return false;
- }
-
- char buffer[512];
- while (fgets(buffer, sizeof(buffer), antCommand) != 0) {
- fprintf(stdout, "%s", buffer);
- fflush(stdout);
- }
-
- int errorCode = pclose(antCommand);
- if (errorCode != 0) {
- fprintf(stderr, "Building the android package failed!\n");
- if (!options.verbose)
- fprintf(stderr, " -- For more information, run this command with --verbose.\n");
- return false;
- }
-
- if (!QDir::setCurrent(oldPath)) {
- fprintf(stderr, "Cannot change back to old path: %s\n", qPrintable(oldPath));
- return false;
- }
-
- return true;
-}
-
typedef QMap<QByteArray, QByteArray> GradleProperties;
static GradleProperties readGradleProperties(const QString &path)
@@ -2335,7 +2238,7 @@ static bool mergeGradleProperties(const QString &path, GradleProperties properti
return true;
}
-bool buildGradleProject(const Options &options)
+bool buildAndroidProject(const Options &options)
{
GradleProperties localProperties;
localProperties["sdk.dir"] = options.sdkPath.toLocal8Bit();
@@ -2403,12 +2306,6 @@ bool buildGradleProject(const Options &options)
return true;
}
-bool buildAndroidProject(const Options &options)
-{
- return options.gradle ? buildGradleProject(options)
- : buildAntProject(options);
-}
-
bool uninstallApk(const Options &options)
{
if (options.verbose)
@@ -2445,15 +2342,11 @@ enum PackageType {
QString apkPath(const Options &options, PackageType pt)
{
QString path(options.outputDirectory);
- if (options.gradle) {
- path += QLatin1String("/build/outputs/apk/");
- QString buildType(options.releasePackage ? QLatin1String("release/") : QLatin1String("debug/"));
- if (QDir(path + buildType).exists())
- path += buildType;
- path += QDir(options.outputDirectory).dirName() + QLatin1Char('-');
- } else {
- path += QLatin1String("/bin/QtApp-");
- }
+ path += QLatin1String("/build/outputs/apk/");
+ QString buildType(options.releasePackage ? QLatin1String("release/") : QLatin1String("debug/"));
+ if (QDir(path + buildType).exists())
+ path += buildType;
+ path += QDir(options.outputDirectory).dirName() + QLatin1Char('-');
if (options.releasePackage) {
path += QLatin1String("release-");
if (pt == UnsignedAPK)
@@ -2503,6 +2396,14 @@ bool installApk(const Options &options)
return true;
}
+bool copyPackage(const Options &options)
+{
+ fflush(stdout);
+ auto from = apkPath(options, options.keyStore.isEmpty() ? UnsignedAPK : SignedAPK);
+ QFile::remove(options.apkPath);
+ return QFile::copy(from, options.apkPath);
+}
+
bool copyStdCpp(Options *options)
{
if (options->verbose)
@@ -2873,7 +2774,8 @@ enum ErrorCode
CannotSignPackage = 15,
CannotInstallApk = 16,
CannotGenerateAssetsFileList = 18,
- CannotCopyAndroidExtraResources = 19
+ CannotCopyAndroidExtraResources = 19,
+ CannotCopyApk = 20
};
int main(int argc, char *argv[])
@@ -2930,8 +2832,7 @@ int main(int argc, char *argv[])
}
if (options.build) {
- if (options.gradle)
- cleanAndroidFiles(options);
+ cleanAndroidFiles(options);
if (Q_UNLIKELY(options.timing))
fprintf(stdout, "[TIMING] %d ms: Cleaned Android file\n", options.timer.elapsed());
@@ -3005,9 +2906,6 @@ int main(int argc, char *argv[])
if (Q_UNLIKELY(options.timing))
fprintf(stdout, "[TIMING] %d ms: Updated files\n", options.timer.elapsed());
- if (!options.gradle && !createAndroidProject(options))
- return CannotCreateAndroidProject;
-
if (Q_UNLIKELY(options.timing))
fprintf(stdout, "[TIMING] %d ms: Created project\n", options.timer.elapsed());
@@ -3020,6 +2918,9 @@ int main(int argc, char *argv[])
if (!options.keyStore.isEmpty() && !signPackage(options))
return CannotSignPackage;
+ if (!options.apkPath.isEmpty() && !copyPackage(options))
+ return CannotCopyApk;
+
if (Q_UNLIKELY(options.timing))
fprintf(stdout, "[TIMING] %d ms: Signed package\n", options.timer.elapsed());
}
diff --git a/src/tools/androidtestrunner/androidtestrunner.pro b/src/tools/androidtestrunner/androidtestrunner.pro
new file mode 100644
index 0000000000..641d3e0003
--- /dev/null
+++ b/src/tools/androidtestrunner/androidtestrunner.pro
@@ -0,0 +1,13 @@
+option(host_build)
+CONFIG += console
+
+SOURCES += \
+ main.cpp
+
+# Required for declarations of popen/pclose on Windows
+windows: QMAKE_CXXFLAGS += -U__STRICT_ANSI__
+
+DEFINES += QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII
+DEFINES += QT_NO_FOREACH
+
+load(qt_app)
diff --git a/src/tools/androidtestrunner/main.cpp b/src/tools/androidtestrunner/main.cpp
new file mode 100644
index 0000000000..bb69b7b914
--- /dev/null
+++ b/src/tools/androidtestrunner/main.cpp
@@ -0,0 +1,490 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 BogDan Vatra <bogdan@kde.org>
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QCoreApplication>
+#include <QDir>
+#include <QHash>
+#include <QRegExp>
+#include <QSystemSemaphore>
+#include <QXmlStreamReader>
+
+#include <algorithm>
+#include <chrono>
+#include <functional>
+#include <thread>
+
+#ifdef Q_CC_MSVC
+#define popen _popen
+#define QT_POPEN_READ "rb"
+#define pclose _pclose
+#else
+#define QT_POPEN_READ "r"
+#endif
+
+struct Options
+{
+ bool helpRequested = false;
+ bool verbose = false;
+ std::chrono::seconds timeout{300}; // 5minutes
+ QString androidDeployQtCommand;
+ QString buildPath;
+ QString adbCommand{QStringLiteral("adb")};
+ QString makeCommand;
+ QString package;
+ QString activity;
+ QStringList testArgsList;
+ QHash<QString, QString> outFiles;
+ QString testArgs;
+ QString apkPath;
+ QHash<QString, std::function<bool(const QByteArray &)>> checkFiles = {
+ {QStringLiteral("txt"), [](const QByteArray &data) -> bool {
+ return data.indexOf("\nFAIL! : ") < 0;
+ }},
+ {QStringLiteral("csv"), [](const QByteArray &/*data*/) -> bool {
+ // It seems csv is broken
+ return true;
+ }},
+ {QStringLiteral("xml"), [](const QByteArray &data) -> bool {
+ QXmlStreamReader reader{data};
+ while (!reader.atEnd()) {
+ reader.readNext();
+ if (reader.isStartElement() && reader.name() == QStringLiteral("Incident") &&
+ reader.attributes().value(QStringLiteral("type")).toString() == QStringLiteral("fail")) {
+ return false;
+ }
+ }
+ return true;
+ }},
+ {QStringLiteral("lightxml"), [](const QByteArray &data) -> bool {
+ return data.indexOf("\n<Incident type=\"fail\" ") < 0;
+ }},
+ {QStringLiteral("xunitxml"), [](const QByteArray &data) -> bool {
+ QXmlStreamReader reader{data};
+ while (!reader.atEnd()) {
+ reader.readNext();
+ if (reader.isStartElement() && reader.name() == QStringLiteral("testcase") &&
+ reader.attributes().value(QStringLiteral("result")).toString() == QStringLiteral("fail")) {
+ return false;
+ }
+ }
+ return true;
+ }},
+ {QStringLiteral("teamcity"), [](const QByteArray &data) -> bool {
+ return data.indexOf("' message='Failure! |[Loc: ") < 0;
+ }},
+ {QStringLiteral("tap"), [](const QByteArray &data) -> bool {
+ return data.indexOf("\nnot ok ") < 0;
+ }},
+ };
+};
+
+static Options g_options;
+
+static bool execCommand(const QString &command, QByteArray *output = nullptr, bool verbose = false)
+{
+ if (verbose)
+ fprintf(stdout, "Execute %s\n", command.toUtf8().constData());
+ FILE *process = popen(command.toUtf8().constData(), QT_POPEN_READ);
+
+ if (!process) {
+ fprintf(stderr, "Cannot execute command %s", qPrintable(command));
+ return false;
+ }
+ char buffer[512];
+ while (fgets(buffer, sizeof(buffer), process)) {
+ if (output)
+ output->append(buffer);
+ if (verbose)
+ fprintf(stdout, "%s", buffer);
+ }
+ return pclose(process) == 0;
+}
+
+// Copy-pasted from qmake/library/ioutil.cpp
+inline static bool hasSpecialChars(const QString &arg, const uchar (&iqm)[16])
+{
+ for (int x = arg.length() - 1; x >= 0; --x) {
+ ushort c = arg.unicode()[x].unicode();
+ if ((c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7))))
+ return true;
+ }
+ return false;
+}
+
+static QString shellQuoteUnix(const QString &arg)
+{
+ // Chars that should be quoted (TM). This includes:
+ static const uchar iqm[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xdf, 0x07, 0x00, 0xd8,
+ 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x78
+ }; // 0-32 \'"$`<>|;&(){}*?#!~[]
+
+ if (!arg.length())
+ return QStringLiteral("\"\"");
+
+ QString ret(arg);
+ if (hasSpecialChars(ret, iqm)) {
+ ret.replace(QLatin1Char('\''), QStringLiteral("'\\''"));
+ ret.prepend(QLatin1Char('\''));
+ ret.append(QLatin1Char('\''));
+ }
+ return ret;
+}
+
+static QString shellQuoteWin(const QString &arg)
+{
+ // Chars that should be quoted (TM). This includes:
+ // - control chars & space
+ // - the shell meta chars "&()<>^|
+ // - the potential separators ,;=
+ static const uchar iqm[] = {
+ 0xff, 0xff, 0xff, 0xff, 0x45, 0x13, 0x00, 0x78,
+ 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10
+ };
+
+ if (!arg.length())
+ return QStringLiteral("\"\"");
+
+ QString ret(arg);
+ if (hasSpecialChars(ret, iqm)) {
+ // Quotes are escaped and their preceding backslashes are doubled.
+ // It's impossible to escape anything inside a quoted string on cmd
+ // level, so the outer quoting must be "suspended".
+ ret.replace(QRegExp(QStringLiteral("(\\\\*)\"")), QStringLiteral("\"\\1\\1\\^\"\""));
+ // The argument must not end with a \ since this would be interpreted
+ // as escaping the quote -- rather put the \ behind the quote: e.g.
+ // rather use "foo"\ than "foo\"
+ int i = ret.length();
+ while (i > 0 && ret.at(i - 1) == QLatin1Char('\\'))
+ --i;
+ ret.insert(i, QLatin1Char('"'));
+ ret.prepend(QLatin1Char('"'));
+ }
+ return ret;
+}
+
+static QString shellQuote(const QString &arg)
+{
+ if (QDir::separator() == QLatin1Char('\\'))
+ return shellQuoteWin(arg);
+ else
+ return shellQuoteUnix(arg);
+}
+
+static bool parseOptions()
+{
+ QStringList arguments = QCoreApplication::arguments();
+ int i = 1;
+ for (; i < arguments.size(); ++i) {
+ const QString &argument = arguments.at(i);
+ if (argument.compare(QStringLiteral("--androiddeployqt"), Qt::CaseInsensitive) == 0) {
+ if (i + 1 == arguments.size())
+ g_options.helpRequested = true;
+ else
+ g_options.androidDeployQtCommand = arguments.at(++i).trimmed();
+ } else if (argument.compare(QStringLiteral("--adb"), Qt::CaseInsensitive) == 0) {
+ if (i + 1 == arguments.size())
+ g_options.helpRequested = true;
+ else
+ g_options.adbCommand = arguments.at(++i);
+ } else if (argument.compare(QStringLiteral("--path"), Qt::CaseInsensitive) == 0) {
+ if (i + 1 == arguments.size())
+ g_options.helpRequested = true;
+ else
+ g_options.buildPath = arguments.at(++i);
+ } else if (argument.compare(QStringLiteral("--make"), Qt::CaseInsensitive) == 0) {
+ if (i + 1 == arguments.size())
+ g_options.helpRequested = true;
+ else
+ g_options.makeCommand = arguments.at(++i);
+ } else if (argument.compare(QStringLiteral("--apk"), Qt::CaseInsensitive) == 0) {
+ if (i + 1 == arguments.size())
+ g_options.helpRequested = true;
+ else
+ g_options.apkPath = arguments.at(++i);
+ } else if (argument.compare(QStringLiteral("--activity"), Qt::CaseInsensitive) == 0) {
+ if (i + 1 == arguments.size())
+ g_options.helpRequested = true;
+ else
+ g_options.activity = arguments.at(++i);
+ } else if (argument.compare(QStringLiteral("--timeout"), Qt::CaseInsensitive) == 0) {
+ if (i + 1 == arguments.size())
+ g_options.helpRequested = true;
+ else
+ g_options.timeout = std::chrono::seconds{arguments.at(++i).toInt()};
+ } else if (argument.compare(QStringLiteral("--help"), Qt::CaseInsensitive) == 0) {
+ g_options.helpRequested = true;
+ } else if (argument.compare(QStringLiteral("--verbose"), Qt::CaseInsensitive) == 0) {
+ g_options.verbose = true;
+ } else if (argument.compare(QStringLiteral("--"), Qt::CaseInsensitive) == 0) {
+ ++i;
+ break;
+ } else {
+ g_options.testArgsList << arguments.at(i);
+ }
+ }
+ for (;i < arguments.size(); ++i)
+ g_options.testArgsList << arguments.at(i);
+
+ if (g_options.helpRequested || g_options.androidDeployQtCommand.isEmpty() || g_options.buildPath.isEmpty())
+ return false;
+
+ QString serial = qEnvironmentVariable("ANDROID_DEVICE_SERIAL");
+ if (!serial.isEmpty())
+ g_options.adbCommand += QStringLiteral(" -s %1").arg(serial);
+ return true;
+}
+
+static void printHelp()
+{// "012345678901234567890123456789012345678901234567890123456789012345678901"
+ fprintf(stderr, "Syntax: %s <options> -- [TESTARGS] \n"
+ "\n"
+ " Creates an Android package in a temp directory <destination> and\n"
+ " runs it on the default emulator/device or on the one specified by\n"
+ " \"ANDROID_DEVICE_SERIAL\" environment variable.\n\n"
+ " Mandatory arguments:\n"
+ " --androiddeployqt <androiddeployqt cmd>: The androiddeployqt:\n"
+ " path including its additional arguments.\n"
+ " --path <path>: The path where androiddeployqt will build the .apk.\n"
+ " Optional arguments:\n"
+ " --adb <adb cmd>: The Android ADB command. If missing the one from\n"
+ " $PATH will be used.\n"
+ " --activity <acitvity>: The Activity to run. If missing the first\n"
+ " activity from AndroidManifest.qml file will be used.\n"
+ " --timout <seconds>: Timeout to run the test.\n"
+ " Default is 5 minutes.\n"
+ " --make <make cmd>: make command, needed to install the qt library.\n"
+ " If make is missing make sure the --path is set.\n"
+ " --apk <apk path>: If the apk is specified and if exists, we'll skip\n"
+ " the package building.\n"
+ " -- arguments that will be passed to the test application.\n"
+ " --verbose: Prints out information during processing.\n"
+ " --help: Displays this information.\n\n",
+ qPrintable(QCoreApplication::arguments().at(0))
+ );
+}
+
+static QString packageNameFromAndroidManifest(const QString &androidManifestPath)
+{
+ QFile androidManifestXml(androidManifestPath);
+ if (androidManifestXml.open(QIODevice::ReadOnly)) {
+ QXmlStreamReader reader(&androidManifestXml);
+ while (!reader.atEnd()) {
+ reader.readNext();
+ if (reader.isStartElement() && reader.name() == QStringLiteral("manifest"))
+ return reader.attributes().value(QStringLiteral("package")).toString();
+ }
+ }
+ return {};
+}
+
+static QString activityFromAndroidManifest(const QString &androidManifestPath)
+{
+ QFile androidManifestXml(androidManifestPath);
+ if (androidManifestXml.open(QIODevice::ReadOnly)) {
+ QXmlStreamReader reader(&androidManifestXml);
+ while (!reader.atEnd()) {
+ reader.readNext();
+ if (reader.isStartElement() && reader.name() == QStringLiteral("activity"))
+ return reader.attributes().value(QStringLiteral("android:name")).toString();
+ }
+ }
+ return {};
+}
+
+static void setOutputFile(QString file, QString format)
+{
+ if (file.isEmpty())
+ file = QStringLiteral("-");
+ if (format.isEmpty())
+ format = QStringLiteral("txt");
+
+ g_options.outFiles[format] = file;
+}
+
+static bool parseTestArgs()
+{
+ QRegExp newLoggingFormat{QStringLiteral("(.*),(txt|csv|xunitxml|xml|lightxml|teamcity|tap)")};
+ QRegExp oldFormats{QStringLiteral("-(txt|csv|xunitxml|xml|lightxml|teamcity|tap)")};
+
+ QString file;
+ QString logType;
+ QString unhandledArgs;
+ for (int i = 0; i < g_options.testArgsList.size(); ++i) {
+ const QString &arg = g_options.testArgsList[i].trimmed();
+ if (arg == QStringLiteral("-o")) {
+ if (i >= g_options.testArgsList.size() - 1)
+ return false; // missing file argument
+
+ const auto &filePath = g_options.testArgsList[++i];
+ if (!newLoggingFormat.exactMatch(filePath)) {
+ file = filePath;
+ } else {
+ const auto capturedTexts = newLoggingFormat.capturedTexts();
+ setOutputFile(capturedTexts.at(1), capturedTexts.at(2));
+ }
+ } else if (oldFormats.exactMatch(arg)) {
+ logType = oldFormats.capturedTexts().at(1);
+ } else {
+ unhandledArgs += QStringLiteral(" %1").arg(arg);
+ }
+ }
+ if (g_options.outFiles.isEmpty() || !file.isEmpty() || !logType.isEmpty())
+ setOutputFile(file, logType);
+
+ for (const auto &format : g_options.outFiles.keys())
+ g_options.testArgs += QStringLiteral(" -o output.%1,%1").arg(format);
+
+ g_options.testArgs += unhandledArgs;
+ g_options.testArgs = QStringLiteral("shell am start -e applicationArguments \"'%1'\" -n %2/%3").arg(shellQuote(g_options.testArgs.trimmed()),
+ g_options.package,
+ g_options.activity);
+ return true;
+}
+
+static bool isRunning() {
+ QByteArray output;
+ if (!execCommand(QStringLiteral("%1 shell \"ps | grep ' %2'\"").arg(g_options.adbCommand,
+ shellQuote(g_options.package)), &output)) {
+
+ return false;
+ }
+ return output.indexOf(" " + g_options.package.toUtf8()) > -1;
+}
+
+static bool waitToFinish()
+{
+ using clock = std::chrono::system_clock;
+ auto start = clock::now();
+ // wait to start
+ while (!isRunning()) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ if ((clock::now() - start) > std::chrono::seconds{10})
+ return false;
+ }
+
+ // Wait to finish
+ while (isRunning()) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(250));
+ if ((clock::now() - start) > g_options.timeout)
+ return false;
+ }
+ return true;
+}
+
+
+static bool pullFiles()
+{
+ bool ret = true;
+ for (auto it = g_options.outFiles.constBegin(); it != g_options.outFiles.end(); ++it) {
+ QByteArray output;
+ if (!execCommand(QStringLiteral("%1 shell run-as %2 cat files/output.%3")
+ .arg(g_options.adbCommand, g_options.package, it.key()), &output)) {
+ return false;
+ }
+ auto checkerIt = g_options.checkFiles.find(it.key());
+ ret &= checkerIt != g_options.checkFiles.end() && checkerIt.value()(output);
+ if (it.value() == QStringLiteral("-")){
+ fprintf(stdout, "%s", output.constData());
+ fflush(stdout);
+ } else {
+ QFile out{it.value()};
+ if (!out.open(QIODevice::WriteOnly))
+ return false;
+ out.write(output);
+ }
+ }
+ return ret;
+}
+
+struct RunnerLocker
+{
+ RunnerLocker()
+ {
+ runner.acquire();
+ }
+ ~RunnerLocker()
+ {
+ runner.release();
+ }
+ QSystemSemaphore runner{QStringLiteral("androidtestrunner"), 1, QSystemSemaphore::Open};
+};
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication a(argc, argv);
+ if (!parseOptions()) {
+ printHelp();
+ return 1;
+ }
+
+ RunnerLocker lock; // do not install or run packages while another test is running
+ if (!g_options.apkPath.isEmpty() && QFile::exists(g_options.apkPath)) {
+ if (!execCommand(QStringLiteral("%1 install -r %2")
+ .arg(g_options.adbCommand, g_options.apkPath), nullptr, g_options.verbose)) {
+ return 1;
+ }
+ } else {
+ if (!g_options.makeCommand.isEmpty()) {
+ // we need to run make INSTALL_ROOT=path install to install the application file(s) first
+ if (!execCommand(QStringLiteral("%1 INSTALL_ROOT=%2 install")
+ .arg(g_options.makeCommand, g_options.buildPath), nullptr, g_options.verbose)) {
+ return 1;
+ }
+ }
+
+ // Run androiddeployqt
+ static auto verbose = g_options.verbose ? QStringLiteral("--verbose") : QStringLiteral();
+ if (!execCommand(QStringLiteral("%1 %3 --reinstall --output %2 --apk %4").arg(g_options.androidDeployQtCommand,
+ g_options.buildPath,
+ verbose,
+ g_options.apkPath), nullptr, true)) {
+ return 1;
+ }
+ }
+
+ QString manifest = g_options.buildPath + QStringLiteral("/AndroidManifest.xml");
+ g_options.package = packageNameFromAndroidManifest(manifest);
+ if (g_options.activity.isEmpty())
+ g_options.activity = activityFromAndroidManifest(manifest);
+
+ // parseTestArgs depends on g_options.package
+ if (!parseTestArgs())
+ return 1;
+
+ // start the tests
+ bool res = execCommand(QStringLiteral("%1 %2").arg(g_options.adbCommand, g_options.testArgs),
+ nullptr, g_options.verbose) && waitToFinish();
+ if (res)
+ res &= pullFiles();
+ res &= execCommand(QStringLiteral("%1 uninstall %2").arg(g_options.adbCommand, g_options.package),
+ nullptr, g_options.verbose);
+ fflush(stdout);
+ return res ? 0 : 1;
+}
diff --git a/src/tools/bootstrap/bootstrap.pro b/src/tools/bootstrap/bootstrap.pro
index 40c0702f0a..8598fc2721 100644
--- a/src/tools/bootstrap/bootstrap.pro
+++ b/src/tools/bootstrap/bootstrap.pro
@@ -60,6 +60,8 @@ SOURCES += \
../../corelib/kernel/qmetatype.cpp \
../../corelib/kernel/qvariant.cpp \
../../corelib/kernel/qsystemerror.cpp \
+ ../../corelib/kernel/qsharedmemory.cpp \
+ ../../corelib/kernel/qsystemsemaphore.cpp \
../../corelib/plugin/quuid.cpp \
../../corelib/serialization/qdatastream.cpp \
../../corelib/serialization/qjson.cpp \
@@ -103,6 +105,12 @@ SOURCES += \
../../xml/sax/qxml.cpp
unix:SOURCES += ../../corelib/kernel/qcore_unix.cpp \
+ ../../corelib/kernel/qsharedmemory_posix.cpp \
+ ../../corelib/kernel/qsharedmemory_systemv.cpp \
+ ../../corelib/kernel/qsharedmemory_unix.cpp \
+ ../../corelib/kernel/qsystemsemaphore_posix.cpp \
+ ../../corelib/kernel/qsystemsemaphore_systemv.cpp \
+ ../../corelib/kernel/qsystemsemaphore_unix.cpp \
../../corelib/io/qfilesystemengine_unix.cpp \
../../corelib/io/qfilesystemiterator_unix.cpp \
../../corelib/io/qfsfileengine_unix.cpp
@@ -112,12 +120,18 @@ win32:SOURCES += ../../corelib/global/qoperatingsystemversion_win.cpp \
../../corelib/io/qfilesystemiterator_win.cpp \
../../corelib/io/qfsfileengine_win.cpp \
../../corelib/kernel/qcoreapplication_win.cpp \
+ ../../corelib/kernel/qsharedmemory_win.cpp \
+ ../../corelib/kernel/qsystemsemaphore_win.cpp \
../../corelib/plugin/qsystemlibrary.cpp \
mac {
SOURCES += \
../../corelib/kernel/qcoreapplication_mac.cpp \
- ../../corelib/kernel/qcore_mac.cpp
+ ../../corelib/kernel/qcore_mac.cpp \
+ ../../corelib/io/qfilesystemengine_unix.cpp \
+ ../../corelib/io/qfilesystemiterator_unix.cpp \
+ ../../corelib/io/qfsfileengine_unix.cpp
+
OBJECTIVE_SOURCES += \
../../corelib/global/qoperatingsystemversion_darwin.mm \
../../corelib/kernel/qcore_mac_objc.mm \
diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp
index ab7e400f47..41d0bbf2a1 100644
--- a/src/tools/moc/generator.cpp
+++ b/src/tools/moc/generator.cpp
@@ -1001,7 +1001,7 @@ void Generator::generateMetacall()
needUser |= p.user.endsWith(')');
}
- fprintf(out, "\n#ifndef QT_NO_PROPERTIES\n ");
+ fprintf(out, "\n#ifndef QT_NO_PROPERTIES\n ");
if (needElse)
fprintf(out, "else ");
fprintf(out,
diff --git a/src/tools/qfloat16-tables/gen_qfloat16_tables.cpp b/src/tools/qfloat16-tables/gen_qfloat16_tables.cpp
deleted file mode 100644
index 17fc978039..0000000000
--- a/src/tools/qfloat16-tables/gen_qfloat16_tables.cpp
+++ /dev/null
@@ -1,163 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 by Southwest Research Institute (R)
-** Contact: http://www.qt-project.org/legal
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <qfile.h>
-#include <qdebug.h>
-
-quint32 convertmantissa(qint32 i)
-{
- quint32 m = i << 13; // Zero pad mantissa bits
- quint32 e = 0; // Zero exponent
-
- while (!(m & 0x00800000)) { // While not normalized
- e -= 0x00800000; // Decrement exponent (1<<23)
- m <<= 1; // Shift mantissa
- }
- m &= ~0x00800000; // Clear leading 1 bit
- e += 0x38800000; // Adjust bias ((127-14)<<23)
- return m | e; // Return combined number
-}
-
-// we first build these tables up and then print them out as a separate step in order
-// to more closely map the implementation given in the paper.
-quint32 basetable[512];
-quint32 shifttable[512];
-
-#define PRINTHEX(a) "0x" + QByteArray::number(a,16).toUpper() + "U,\n"
-
-qint32 main(qint32 argc, char **argv)
-{
- if (argc < 2) {
- qWarning() << "Must provide output filename as argument.";
- return -1;
- }
-
- QFile fid(QFile::decodeName(argv[1]));
- if (!fid.open(QIODevice::WriteOnly | QIODevice::Text)) {
- qWarning() << "Abort: Failed to open/create file" << fid.fileName();
- return -1;
- }
- quint32 i;
-
- fid.write("/* This file was generated by gen_qfloat16_tables.cpp */\n\n");
- fid.write("#include <QtCore/qfloat16.h>\n\n");
-
- fid.write("QT_BEGIN_NAMESPACE\n\n");
- fid.write("#if !defined(__F16C__) && !defined(__ARM_FP16_FORMAT_IEEE)\n\n");
-
- fid.write("const quint32 qfloat16::mantissatable[2048] = {\n");
- fid.write("0,\n");
- for (i = 1; i < 1024; i++)
- fid.write(PRINTHEX(convertmantissa(i)));
- for (i = 1024; i < 2048; i++)
- fid.write(PRINTHEX(0x38000000U + ((i - 1024) << 13)));
- fid.write("};\n\n");
-
- fid.write("const quint32 qfloat16::exponenttable[64] = {\n");
- fid.write("0,\n");
- for (i = 1; i < 31; i++)
- fid.write(PRINTHEX(i << 23));
- fid.write("0x47800000U,\n"); // 31
- fid.write("0x80000000U,\n"); // 32
- for (i = 33; i < 63; i++)
- fid.write(PRINTHEX(0x80000000U + ((i - 32) << 23)));
- fid.write("0xC7800000U,\n"); // 63
- fid.write("};\n\n");
-
- fid.write("const quint32 qfloat16::offsettable[64] = {\n");
- fid.write("0,\n");
- for (i = 1; i < 32; i++)
- fid.write("1024U,\n");
- fid.write("0,\n");
- for (i = 33; i < 64; i++)
- fid.write("1024U,\n");
- fid.write("};\n\n");
-
- qint32 e;
- for (i = 0; i < 256; ++i) {
- e = i - 127;
- if (e < -24) { // Very small numbers map to zero
- basetable[i | 0x000] = 0x0000;
- basetable[i | 0x100] = 0x8000;
- shifttable[i | 0x000] = 24;
- shifttable[i | 0x100] = 24;
-
- } else if (e < -14) { // Small numbers map to denorms
- basetable[i | 0x000] = (0x0400 >> (-e - 14));
- basetable[i | 0x100] = (0x0400 >> (-e - 14)) | 0x8000;
- shifttable[i | 0x000] = -e - 1;
- shifttable[i | 0x100] = -e - 1;
-
- } else if (e <= 15) { // Normal numbers just lose precision
- basetable[i | 0x000] = ((e + 15) << 10);
- basetable[i | 0x100] = ((e + 15) << 10) | 0x8000;
- shifttable[i | 0x000] = 13;
- shifttable[i | 0x100] = 13;
-
- } else if (e < 128) { // Large numbers map to Infinity
- basetable[i | 0x000] = 0x7C00;
- basetable[i | 0x100] = 0xFC00;
- shifttable[i | 0x000] = 24;
- shifttable[i | 0x100] = 24;
-
- } else { // Infinity and NaN's stay Infinity and NaN's
- basetable[i | 0x000] = 0x7C00;
- basetable[i | 0x100] = 0xFC00;
- shifttable[i | 0x000] = 13;
- shifttable[i | 0x100] = 13;
- }
- }
-
- fid.write("const quint32 qfloat16::basetable[512] = {\n");
- for (i = 0; i < 512; i++)
- fid.write(PRINTHEX(basetable[i]));
-
- fid.write("};\n\n");
-
- fid.write("const quint32 qfloat16::shifttable[512] = {\n");
- for (i = 0; i < 512; i++)
- fid.write(PRINTHEX(shifttable[i]));
-
- fid.write("};\n\n");
-
- fid.write("#endif // !__F16C__ && !__ARM_FP16_FORMAT_IEEE\n\n");
- fid.write("QT_END_NAMESPACE\n");
- fid.close();
- return 0;
-}
diff --git a/src/tools/qfloat16-tables/qfloat16-tables.pro b/src/tools/qfloat16-tables/qfloat16-tables.pro
deleted file mode 100644
index a7d10ac197..0000000000
--- a/src/tools/qfloat16-tables/qfloat16-tables.pro
+++ /dev/null
@@ -1,9 +0,0 @@
-option(host_build)
-
-CONFIG += force_bootstrap
-SOURCES += gen_qfloat16_tables.cpp
-
-load(qt_tool)
-
-lib.CONFIG = dummy_install
-INSTALLS = lib
diff --git a/src/tools/rcc/rcc.cpp b/src/tools/rcc/rcc.cpp
index 08fb6fca5f..94f6911010 100644
--- a/src/tools/rcc/rcc.cpp
+++ b/src/tools/rcc/rcc.cpp
@@ -67,11 +67,8 @@ enum {
# define CONSTANT_COMPRESSALGO_DEFAULT RCCResourceLibrary::CompressionAlgorithm::None
#endif
-#define writeString(s) write(s, sizeof(s))
-
void RCCResourceLibrary::write(const char *str, int len)
{
- --len; // trailing \0 on string literals...
int n = m_out.size();
m_out.resize(n + len);
memcpy(m_out.data() + n, str, len);
@@ -983,7 +980,7 @@ void RCCResourceLibrary::writeDecimal(int value)
Q_ASSERT(m_format != RCCResourceLibrary::Binary);
char buf[std::numeric_limits<int>::digits10 + 2];
int n = snprintf(buf, sizeof(buf), "%d", value);
- write(buf, n + 1); // write() takes a size including terminating NUL
+ write(buf, n);
}
static const char hexDigits[] = "0123456789abcdef";
diff --git a/src/tools/rcc/rcc.h b/src/tools/rcc/rcc.h
index b301355e4f..190c37a1f6 100644
--- a/src/tools/rcc/rcc.h
+++ b/src/tools/rcc/rcc.h
@@ -143,6 +143,7 @@ private:
void writeChar(char c) { m_out.append(c); }
void writeByteArray(const QByteArray &);
void write(const char *, int len);
+ void writeString(const char *s) { write(s, static_cast<int>(strlen(s))); }
#if QT_CONFIG(zstd)
ZSTD_CCtx *m_zstdCCtx;
diff --git a/src/tools/uic/cpp/cppwriteincludes.h b/src/tools/uic/cpp/cppwriteincludes.h
index 79cbd41014..aadc6f54fc 100644
--- a/src/tools/uic/cpp/cppwriteincludes.h
+++ b/src/tools/uic/cpp/cppwriteincludes.h
@@ -82,7 +82,7 @@ private:
void add(const QString &className, bool determineHeader = true, const QString &header = QString(), bool global = false);
private:
- typedef std::set<QString> OrderedSet;
+ using OrderedSet = std::set<QString>;
void insertIncludeForClass(const QString &className, QString header = QString(), bool global = false);
void insertInclude(const QString &header, bool global);
void writeHeaders(const OrderedSet &headers, bool global);
@@ -97,7 +97,7 @@ private:
QSet<QString> m_knownClasses;
- typedef QMap<QString, QString> StringMap;
+ using StringMap = QMap<QString, QString>;
StringMap m_classToHeader;
StringMap m_oldHeaderToNewHeader;
diff --git a/src/tools/uic/cpp/cppwriteinitialization.cpp b/src/tools/uic/cpp/cppwriteinitialization.cpp
index 440758cf41..85b9a9f60b 100644
--- a/src/tools/uic/cpp/cppwriteinitialization.cpp
+++ b/src/tools/uic/cpp/cppwriteinitialization.cpp
@@ -498,9 +498,7 @@ void WriteInitialization::acceptUI(DomUI *node)
<< language::startFunctionDefinition1("setupUi", parameterType, varName, m_option.indent);
const QStringList connections = m_uic->databaseInfo()->connections();
- for (int i=0; i<connections.size(); ++i) {
- QString connection = connections.at(i);
-
+ for (const auto &connection : connections) {
if (connection == QLatin1String("(default)"))
continue;
@@ -706,7 +704,7 @@ void WriteInitialization::acceptWidget(DomWidget *node)
addWizardPage(varName, node, parentWidget);
} else if (m_uic->customWidgetsInfo()->extends(parentClass, QLatin1String("QToolBox"))) {
const DomProperty *plabel = attributes.value(QLatin1String("label"));
- DomString *plabelString = plabel ? plabel->elementString() : 0;
+ DomString *plabelString = plabel ? plabel->elementString() : nullptr;
QString icon;
if (const DomProperty *picon = attributes.value(QLatin1String("icon")))
icon = QLatin1String(", ") + iconCall(picon); // Side effect: Writes icon definition
@@ -729,7 +727,7 @@ void WriteInitialization::acceptWidget(DomWidget *node)
}
} else if (m_uic->customWidgetsInfo()->extends(parentClass, QLatin1String("QTabWidget"))) {
const DomProperty *ptitle = attributes.value(QLatin1String("title"));
- DomString *ptitleString = ptitle ? ptitle->elementString() : 0;
+ DomString *ptitleString = ptitle ? ptitle->elementString() : nullptr;
QString icon;
if (const DomProperty *picon = attributes.value(QLatin1String("icon")))
icon = QLatin1String(", ") + iconCall(picon); // Side effect: Writes icon definition
@@ -844,7 +842,7 @@ void WriteInitialization::addButtonGroup(const DomWidget *buttonNode, const QStr
const DomButtonGroup *group = m_driver->findButtonGroup(attributeName);
// Legacy feature: Create missing groups on the fly as the UIC button group feature
// was present before the actual Designer support (4.5)
- const bool createGroupOnTheFly = group == 0;
+ const bool createGroupOnTheFly = group == nullptr;
if (createGroupOnTheFly) {
DomButtonGroup *newGroup = new DomButtonGroup;
newGroup->setAttributeName(attributeName);
@@ -900,8 +898,7 @@ void WriteInitialization::acceptLayout(DomLayout *node)
if (m_layoutWidget) {
bool left, top, right, bottom;
left = top = right = bottom = false;
- for (int i = 0; i < propList.size(); ++i) {
- const DomProperty *p = propList.at(i);
+ for (const DomProperty *p : propList) {
const QString propertyName = p->attributeName();
if (propertyName == QLatin1String("leftMargin") && p->kind() == DomProperty::Number)
left = true;
@@ -2619,7 +2616,6 @@ static void generateMultiDirectiveEnd(QTextStream &outputStream, const QSet<QStr
WriteInitialization::Item::Item(const QString &itemClassName, const QString &indent, QTextStream &setupUiStream, QTextStream &retranslateUiStream, Driver *driver)
:
- m_parent(0),
m_itemClassName(itemClassName),
m_indent(indent),
m_setupUiStream(setupUiStream),
diff --git a/src/tools/uic/cpp/cppwriteinitialization.h b/src/tools/uic/cpp/cppwriteinitialization.h
index 0ee001469c..3cd0efeaac 100644
--- a/src/tools/uic/cpp/cppwriteinitialization.h
+++ b/src/tools/uic/cpp/cppwriteinitialization.h
@@ -85,8 +85,8 @@ namespace CPP {
struct WriteInitialization : public TreeWalker
{
- typedef QList<DomProperty*> DomPropertyList;
- typedef QHash<QString, DomProperty*> DomPropertyMap;
+ using DomPropertyList = QList<DomProperty*>;
+ using DomPropertyMap = QHash<QString, DomProperty*>;
WriteInitialization(Uic *uic);
@@ -161,7 +161,7 @@ private:
// special initialization
//
class Item {
- Q_DISABLE_COPY(Item)
+ Q_DISABLE_COPY_MOVE(Item)
public:
Item(const QString &itemClassName, const QString &indent, QTextStream &setupUiStream, QTextStream &retranslateUiStream, Driver *driver);
~Item();
@@ -188,7 +188,7 @@ private:
ItemData m_setupUiData;
ItemData m_retranslateUiData;
QList<Item *> m_children;
- Item *m_parent;
+ Item *m_parent = nullptr;
const QString m_itemClassName;
const QString m_indent;
@@ -259,13 +259,13 @@ private:
QVector<Buddy> m_buddies;
QSet<QString> m_buttonGroups;
- typedef QHash<uint, QString> ColorBrushHash;
+ using ColorBrushHash = QHash<uint, QString>;
ColorBrushHash m_colorBrushHash;
// Map from font properties to font variable name for reuse
// Map from size policy to variable for reuse
- typedef QMap<FontHandle, QString> FontPropertiesNameMap;
- typedef QMap<IconHandle, QString> IconPropertiesNameMap;
- typedef QMap<SizePolicyHandle, QString> SizePolicyNameMap;
+ using FontPropertiesNameMap = QMap<FontHandle, QString>;
+ using IconPropertiesNameMap = QMap<IconHandle, QString>;
+ using SizePolicyNameMap = QMap<SizePolicyHandle, QString>;
FontPropertiesNameMap m_fontPropertiesNameMap;
IconPropertiesNameMap m_iconPropertiesNameMap;
SizePolicyNameMap m_sizePolicyNameMap;
diff --git a/src/tools/uic/customwidgetsinfo.h b/src/tools/uic/customwidgetsinfo.h
index 8a10999027..a1b24ab042 100644
--- a/src/tools/uic/customwidgetsinfo.h
+++ b/src/tools/uic/customwidgetsinfo.h
@@ -62,7 +62,7 @@ public:
bool isCustomWidgetContainer(const QString &className) const;
private:
- typedef QMap<QString, DomCustomWidget*> NameCustomWidgetMap;
+ using NameCustomWidgetMap = QMap<QString, DomCustomWidget*>;
NameCustomWidgetMap m_customWidgets;
};
diff --git a/src/tools/uic/databaseinfo.cpp b/src/tools/uic/databaseinfo.cpp
index fa5d5f5df0..9b0d1614ab 100644
--- a/src/tools/uic/databaseinfo.cpp
+++ b/src/tools/uic/databaseinfo.cpp
@@ -33,9 +33,7 @@
QT_BEGIN_NAMESPACE
-DatabaseInfo::DatabaseInfo()
-{
-}
+DatabaseInfo::DatabaseInfo() = default;
void DatabaseInfo::acceptUI(DomUI *node)
{
@@ -59,10 +57,9 @@ void DatabaseInfo::acceptWidget(DomWidget *node)
DomProperty *db = properties.value(QLatin1String("database"));
if (db && db->elementStringList()) {
QStringList info = db->elementStringList()->elementString();
-
- QString connection = info.size() > 0 ? info.at(0) : QString();
- if (connection.isEmpty())
+ if (info.isEmpty() || info.constFirst().isEmpty())
return;
+ const QString &connection = info.constFirst();
m_connections.append(connection);
QString table = info.size() > 1 ? info.at(1) : QString();
diff --git a/src/tools/uic/driver.cpp b/src/tools/uic/driver.cpp
index eb88032e59..8b9b4806e6 100644
--- a/src/tools/uic/driver.cpp
+++ b/src/tools/uic/driver.cpp
@@ -256,7 +256,7 @@ bool Driver::uic(const QString &fileName, DomUI *ui, QTextStream *out)
QTextStream *oldOutput = m_output;
- m_output = out != 0 ? out : &m_stdout;
+ m_output = out != nullptr ? out : &m_stdout;
Uic tool(this);
const bool result = tool.write(ui);
diff --git a/src/tools/uic/main.cpp b/src/tools/uic/main.cpp
index e689d477bd..5c30e4d239 100644
--- a/src/tools/uic/main.cpp
+++ b/src/tools/uic/main.cpp
@@ -130,7 +130,7 @@ int runUic(int argc, char *argv[])
return !driver.printDependencies(inputFile);
}
- QTextStream *out = 0;
+ QTextStream *out = nullptr;
QFile f;
if (!driver.option().outputFile.isEmpty()) {
f.setFileName(driver.option().outputFile);
diff --git a/src/tools/uic/treewalker.h b/src/tools/uic/treewalker.h
index 7e8eda57d9..3777229517 100644
--- a/src/tools/uic/treewalker.h
+++ b/src/tools/uic/treewalker.h
@@ -77,6 +77,9 @@ class DomButtonGroup;
struct TreeWalker
{
+ Q_DISABLE_COPY_MOVE(TreeWalker)
+
+ TreeWalker() = default;
inline virtual ~TreeWalker() = default;
virtual void acceptUI(DomUI *ui);
@@ -101,7 +104,7 @@ struct TreeWalker
virtual void acceptTime(DomTime *time);
virtual void acceptDateTime(DomDateTime *dateTime);
virtual void acceptProperty(DomProperty *property);
- typedef QVector<DomWidget *> DomWidgets;
+ using DomWidgets = QVector<DomWidget *>;
virtual void acceptIncludes(DomIncludes *includes);
virtual void acceptInclude(DomInclude *incl);
virtual void acceptAction(DomAction *action);
diff --git a/src/tools/uic/uic.cpp b/src/tools/uic/uic.cpp
index b95f1a784b..207356f28d 100644
--- a/src/tools/uic/uic.cpp
+++ b/src/tools/uic/uic.cpp
@@ -69,7 +69,7 @@ bool Uic::printDependencies()
return false;
}
- DomUI *ui = 0;
+ DomUI *ui = nullptr;
{
QXmlStreamReader reader;
reader.setDevice(&f);
@@ -178,7 +178,7 @@ static double versionFromUiAttribute(QXmlStreamReader &reader)
DomUI *Uic::parseUiFile(QXmlStreamReader &reader)
{
- DomUI *ui = 0;
+ DomUI *ui = nullptr;
const QString uiElement = QLatin1String("ui");
while (!reader.atEnd()) {
@@ -189,7 +189,7 @@ DomUI *Uic::parseUiFile(QXmlStreamReader &reader)
if (version < 4.0) {
const QString msg = QString::fromLatin1("uic: File generated with too old version of Qt Designer (%1)").arg(version);
fprintf(stderr, "%s\n", qPrintable(msg));
- return 0;
+ return nullptr;
}
ui = new DomUI();
@@ -201,7 +201,7 @@ DomUI *Uic::parseUiFile(QXmlStreamReader &reader)
}
if (reader.hasError()) {
delete ui;
- ui = 0;
+ ui = nullptr;
fprintf(stderr, "%s\n", qPrintable(QString::fromLatin1("uic: Error in line %1, column %2 : %3")
.arg(reader.lineNumber()).arg(reader.columnNumber())
.arg(reader.errorString())));
diff --git a/src/widgets/accessible/qaccessiblemenu.cpp b/src/widgets/accessible/qaccessiblemenu.cpp
index 507584eb02..7f87288520 100644
--- a/src/widgets/accessible/qaccessiblemenu.cpp
+++ b/src/widgets/accessible/qaccessiblemenu.cpp
@@ -47,6 +47,7 @@
#endif
#include <QtWidgets/QAction>
#include <qstyle.h>
+#include <private/qwidget_p.h>
#ifndef QT_NO_ACCESSIBILITY
@@ -241,15 +242,9 @@ QObject *QAccessibleMenuItem::object() const
/*! \reimp */
QWindow *QAccessibleMenuItem::window() const
{
- QWindow *result = nullptr;
- if (!m_owner.isNull()) {
- result = m_owner->windowHandle();
- if (!result) {
- if (const QWidget *nativeParent = m_owner->nativeParentWidget())
- result = nativeParent->windowHandle();
- }
- }
- return result;
+ return m_owner.isNull()
+ ? nullptr
+ : qt_widget_private(m_owner.data())->windowHandle(QWidgetPrivate::WindowHandleMode::Closest);
}
QRect QAccessibleMenuItem::rect() const
diff --git a/src/widgets/dialogs/qfileinfogatherer.cpp b/src/widgets/dialogs/qfileinfogatherer.cpp
index 1e03ad8b06..f39ae2b53e 100644
--- a/src/widgets/dialogs/qfileinfogatherer.cpp
+++ b/src/widgets/dialogs/qfileinfogatherer.cpp
@@ -54,12 +54,12 @@ QT_BEGIN_NAMESPACE
static QBasicAtomicInt fetchedRoot = Q_BASIC_ATOMIC_INITIALIZER(false);
Q_AUTOTEST_EXPORT void qt_test_resetFetchedRoot()
{
- fetchedRoot.store(false);
+ fetchedRoot.storeRelaxed(false);
}
Q_AUTOTEST_EXPORT bool qt_test_isFetchedRoot()
{
- return fetchedRoot.load();
+ return fetchedRoot.loadRelaxed();
}
#endif
@@ -111,7 +111,7 @@ QFileInfoGatherer::QFileInfoGatherer(QObject *parent)
*/
QFileInfoGatherer::~QFileInfoGatherer()
{
- abort.store(true);
+ abort.storeRelaxed(true);
QMutexLocker locker(&mutex);
condition.wakeAll();
locker.unlock();
@@ -247,9 +247,9 @@ void QFileInfoGatherer::run()
{
forever {
QMutexLocker locker(&mutex);
- while (!abort.load() && path.isEmpty())
+ while (!abort.loadRelaxed() && path.isEmpty())
condition.wait(&mutex);
- if (abort.load())
+ if (abort.loadRelaxed())
return;
const QString thisPath = qAsConst(path).front();
path.pop_front();
@@ -303,7 +303,7 @@ void QFileInfoGatherer::getFileInfos(const QString &path, const QStringList &fil
// List drives
if (path.isEmpty()) {
#ifdef QT_BUILD_INTERNAL
- fetchedRoot.store(true);
+ fetchedRoot.storeRelaxed(true);
#endif
QFileInfoList infoList;
if (files.isEmpty()) {
@@ -332,7 +332,7 @@ void QFileInfoGatherer::getFileInfos(const QString &path, const QStringList &fil
QStringList allFiles;
if (files.isEmpty()) {
QDirIterator dirIt(path, QDir::AllEntries | QDir::System | QDir::Hidden);
- while (!abort.load() && dirIt.hasNext()) {
+ while (!abort.loadRelaxed() && dirIt.hasNext()) {
dirIt.next();
fileInfo = dirIt.fileInfo();
allFiles.append(fileInfo.fileName());
@@ -343,7 +343,7 @@ void QFileInfoGatherer::getFileInfos(const QString &path, const QStringList &fil
emit newListOfFiles(path, allFiles);
QStringList::const_iterator filesIt = filesToCheck.constBegin();
- while (!abort.load() && filesIt != filesToCheck.constEnd()) {
+ while (!abort.loadRelaxed() && filesIt != filesToCheck.constEnd()) {
fileInfo.setFile(path + QDir::separator() + *filesIt);
++filesIt;
fetch(fileInfo, base, firstTime, updatedFiles, path);
diff --git a/src/widgets/dialogs/qmessagebox.cpp b/src/widgets/dialogs/qmessagebox.cpp
index f143e3b527..9bfea06a54 100644
--- a/src/widgets/dialogs/qmessagebox.cpp
+++ b/src/widgets/dialogs/qmessagebox.cpp
@@ -2662,14 +2662,9 @@ QPixmap QMessageBoxPrivate::standardIcon(QMessageBox::Icon icon, QMessageBox *mb
break;
}
if (!tmpIcon.isNull()) {
- QWindow *window = nullptr;
- if (mb) {
- window = mb->windowHandle();
- if (!window) {
- if (const QWidget *nativeParent = mb->nativeParentWidget())
- window = nativeParent->windowHandle();
- }
- }
+ QWindow *window = mb
+ ? qt_widget_private(mb)->windowHandle(QWidgetPrivate::WindowHandleMode::Closest)
+ : nullptr;
return tmpIcon.pixmap(window, QSize(iconSize, iconSize));
}
return QPixmap();
diff --git a/src/widgets/doc/src/dontdocument.qdoc b/src/widgets/doc/src/dontdocument.qdoc
new file mode 100644
index 0000000000..9de8b5e62d
--- /dev/null
+++ b/src/widgets/doc/src/dontdocument.qdoc
@@ -0,0 +1,30 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \dontdocument (QTypeInfo QMetaTypeId)
+*/
diff --git a/src/widgets/itemviews/qabstractitemview.cpp b/src/widgets/itemviews/qabstractitemview.cpp
index 95631ffa5f..1d1c144bb8 100644
--- a/src/widgets/itemviews/qabstractitemview.cpp
+++ b/src/widgets/itemviews/qabstractitemview.cpp
@@ -4461,15 +4461,8 @@ QPixmap QAbstractItemViewPrivate::renderToPixmap(const QModelIndexList &indexes,
if (paintPairs.isEmpty())
return QPixmap();
- qreal scale = 1.0f;
-
- Q_Q(const QAbstractItemView);
- QWidget *window = q->window();
- if (window) {
- QWindow *windowHandle = window->windowHandle();
- if (windowHandle)
- scale = windowHandle->devicePixelRatio();
- }
+ QWindow *window = windowHandle(WindowHandleMode::Closest);
+ const qreal scale = window ? window->devicePixelRatio() : qreal(1);
QPixmap pixmap(r->size() * scale);
pixmap.setDevicePixelRatio(scale);
diff --git a/src/widgets/itemviews/qheaderview.cpp b/src/widgets/itemviews/qheaderview.cpp
index 99309633a7..1fcd5bdef2 100644
--- a/src/widgets/itemviews/qheaderview.cpp
+++ b/src/widgets/itemviews/qheaderview.cpp
@@ -146,7 +146,10 @@ static const int maxSizeSection = 1048575; // since section size is in a bitfiel
Not all \l{Qt::}{ItemDataRole}s will have an effect on a
QHeaderView. If you need to draw other roles, you can subclass
QHeaderView and reimplement \l{QHeaderView::}{paintEvent()}.
- QHeaderView respects the following item data roles:
+ QHeaderView respects the following item data roles, unless they are
+ in conflict with the style (which can happen for styles that follow
+ the desktop theme):
+
\l{Qt::}{TextAlignmentRole}, \l{Qt::}{DisplayRole},
\l{Qt::}{FontRole}, \l{Qt::}{DecorationRole},
\l{Qt::}{ForegroundRole}, and \l{Qt::}{BackgroundRole}.
diff --git a/src/widgets/kernel/qaction.cpp b/src/widgets/kernel/qaction.cpp
index 1ca5514655..f6631199d6 100644
--- a/src/widgets/kernel/qaction.cpp
+++ b/src/widgets/kernel/qaction.cpp
@@ -1153,8 +1153,9 @@ void QAction::activate(ActionEvent event)
if(event == Trigger) {
QPointer<QObject> guard = this;
if(d->checkable) {
- // the checked action of an exclusive group cannot be unchecked
- if (d->checked && (d->group && d->group->isExclusive()
+ // the checked action of an exclusive group may not be unchecked
+ if (d->checked && (d->group
+ && d->group->exclusionPolicy() == QActionGroup::ExclusionPolicy::Exclusive
&& d->group->checkedAction() == this)) {
if (!guard.isNull())
emit triggered(true);
diff --git a/src/widgets/kernel/qactiongroup.cpp b/src/widgets/kernel/qactiongroup.cpp
index 4786437d7e..ab42b1c7aa 100644
--- a/src/widgets/kernel/qactiongroup.cpp
+++ b/src/widgets/kernel/qactiongroup.cpp
@@ -52,12 +52,16 @@ class QActionGroupPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QActionGroup)
public:
- QActionGroupPrivate() : exclusive(1), enabled(1), visible(1) { }
+ QActionGroupPrivate() : enabled(1),
+ visible(1),
+ exclusionPolicy(QActionGroup::ExclusionPolicy::Exclusive)
+ {
+ }
QList<QAction *> actions;
QPointer<QAction> current;
- uint exclusive : 1;
uint enabled : 1;
uint visible : 1;
+ QActionGroup::ExclusionPolicy exclusionPolicy;
private:
void _q_actionTriggered(); //private slot
@@ -70,7 +74,7 @@ void QActionGroupPrivate::_q_actionChanged()
Q_Q(QActionGroup);
QAction *action = qobject_cast<QAction*>(q->sender());
Q_ASSERT_X(action != 0, "QWidgetGroup::_q_actionChanged", "internal error");
- if(exclusive) {
+ if (exclusionPolicy != QActionGroup::ExclusionPolicy::None) {
if (action->isChecked()) {
if (action != current) {
if(current)
@@ -127,12 +131,17 @@ void QActionGroupPrivate::_q_actionHovered()
actions is chosen. Each action in an action group emits its
triggered() signal as usual.
- As stated above, an action group is \l exclusive by default; it
- ensures that only one checkable action is active at any one time.
+ As stated above, an action group is exclusive by default; it
+ ensures that at most only one checkable action is active at any one time.
If you want to group checkable actions without making them
- exclusive, you can turn of exclusiveness by calling
+ exclusive, you can turn off exclusiveness by calling
setExclusive(false).
+ By default the active action of an exclusive group cannot be unchecked.
+ In some cases it may be useful to allow unchecking all the actions,
+ you can allow this by calling
+ setExclusionPolicy(QActionGroup::ExclusionPolicy::ExclusiveOptional).
+
Actions can be added to an action group using addAction(), but it
is usually more convenient to specify a group when creating
actions; this ensures that actions are automatically created with
@@ -146,10 +155,33 @@ void QActionGroupPrivate::_q_actionHovered()
*/
/*!
+ \enum QActionGroup::ExclusionPolicy
+
+ This enum specifies the different policies that can be used to
+ control how the group performs exclusive checking on checkable actions.
+
+ \value None
+ The actions in the group can be checked independently of each other.
+
+ \value Exclusive
+ Exactly one action can be checked at any one time.
+ This is the default policy.
+
+ \value ExclusiveOptional
+ At most one action can be checked at any one time. The actions
+ can also be all unchecked.
+
+ \sa exclusionPolicy
+ \since 5.14
+*/
+
+/*!
Constructs an action group for the \a parent object.
The action group is exclusive by default. Call setExclusive(false)
- to make the action group non-exclusive.
+ to make the action group non-exclusive. To make the group exclusive
+ but allow unchecking the active action call instead
+ setExclusionPolicy(QActionGroup::ExclusionPolicy::ExclusiveOptional)
*/
QActionGroup::QActionGroup(QObject* parent) : QObject(*new QActionGroupPrivate, parent)
{
@@ -258,26 +290,56 @@ QList<QAction*> QActionGroup::actions() const
}
/*!
- \property QActionGroup::exclusive
- \brief whether the action group does exclusive checking
+ \brief Enable or disable the group exclusion checking
- If exclusive is true, only one checkable action in the action group
- can ever be active at any time. If the user chooses another
- checkable action in the group, the one they chose becomes active and
- the one that was active becomes inactive.
+ This is a convenience method that calls
+ setExclusionPolicy(ExclusionPolicy::Exclusive).
- \sa QAction::checkable
+ \sa QActionGroup::exclusionPolicy
*/
void QActionGroup::setExclusive(bool b)
{
- Q_D(QActionGroup);
- d->exclusive = b;
+ setExclusionPolicy(b ? QActionGroup::ExclusionPolicy::Exclusive
+ : QActionGroup::ExclusionPolicy::None);
}
+/*!
+ \brief Returs true if the group is exclusive
+
+ The group is exclusive if the ExclusionPolicy is either Exclusive
+ or ExclusionOptional.
+
+*/
bool QActionGroup::isExclusive() const
{
+ return exclusionPolicy() != QActionGroup::ExclusionPolicy::None;
+}
+
+/*!
+ \property QActionGroup::exclusionPolicy
+ \brief This property holds the group exclusive checking policy
+
+ If exclusionPolicy is set to Exclusive, only one checkable
+ action in the action group can ever be active at any time. If the user
+ chooses another checkable action in the group, the one they chose becomes
+ active and the one that was active becomes inactive. If exclusionPolicy is
+ set to ExclusionOptional the group is exclusive but the active checkable
+ action in the group can be unchecked leaving the group with no actions
+ checked.
+
+ \sa QAction::checkable
+ \since 5.14
+*/
+void QActionGroup::setExclusionPolicy(QActionGroup::ExclusionPolicy policy)
+{
+ Q_D(QActionGroup);
+ d->exclusionPolicy = policy;
+}
+
+QActionGroup::ExclusionPolicy QActionGroup::exclusionPolicy() const
+{
Q_D(const QActionGroup);
- return d->exclusive;
+ return d->exclusionPolicy;
}
/*!
diff --git a/src/widgets/kernel/qactiongroup.h b/src/widgets/kernel/qactiongroup.h
index 61c90b911d..90f488bedb 100644
--- a/src/widgets/kernel/qactiongroup.h
+++ b/src/widgets/kernel/qactiongroup.h
@@ -55,11 +55,18 @@ class Q_WIDGETS_EXPORT QActionGroup : public QObject
Q_OBJECT
Q_DECLARE_PRIVATE(QActionGroup)
- Q_PROPERTY(bool exclusive READ isExclusive WRITE setExclusive)
+ Q_PROPERTY(QActionGroup::ExclusionPolicy exclusionPolicy READ exclusionPolicy WRITE setExclusionPolicy)
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
Q_PROPERTY(bool visible READ isVisible WRITE setVisible)
public:
+ enum class ExclusionPolicy {
+ None,
+ Exclusive,
+ ExclusiveOptional
+ };
+ Q_ENUM(ExclusionPolicy)
+
explicit QActionGroup(QObject* parent);
~QActionGroup();
@@ -73,6 +80,7 @@ public:
bool isExclusive() const;
bool isEnabled() const;
bool isVisible() const;
+ ExclusionPolicy exclusionPolicy() const;
public Q_SLOTS:
@@ -80,6 +88,7 @@ public Q_SLOTS:
inline void setDisabled(bool b) { setEnabled(!b); }
void setVisible(bool);
void setExclusive(bool);
+ void setExclusionPolicy(ExclusionPolicy policy);
Q_SIGNALS:
void triggered(QAction *);
diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp
index ba8878be57..526bf0a0ff 100644
--- a/src/widgets/kernel/qapplication.cpp
+++ b/src/widgets/kernel/qapplication.cpp
@@ -381,8 +381,6 @@ QWidget *QApplication::topLevelAt(const QPoint &pos)
0 if there is no such widget.
*/
-void qt_init(QApplicationPrivate *priv, int type
- );
void qt_init_tooltip_palette();
void qt_cleanup();
@@ -428,16 +426,10 @@ bool Q_WIDGETS_EXPORT qt_tab_all_widgets()
// ######## move to QApplicationPrivate
// Default application palettes and fonts (per widget type)
Q_GLOBAL_STATIC(PaletteHash, app_palettes)
-PaletteHash *qt_app_palettes_hash()
-{
- return app_palettes();
-}
-
Q_GLOBAL_STATIC(FontHash, app_fonts)
-FontHash *qt_app_fonts_hash()
-{
- return app_fonts();
-}
+// Exported accessors for use outside of this file
+PaletteHash *qt_app_palettes_hash() { return app_palettes(); }
+FontHash *qt_app_fonts_hash() { return app_fonts(); }
QWidgetList *QApplicationPrivate::popupWidgets = 0; // has keyboard input focus
@@ -571,7 +563,10 @@ void QApplicationPrivate::init()
process_cmdline();
// Must be called before initialize()
- qt_init(this, application_type);
+ QColormap::initialize();
+ qt_init_tooltip_palette();
+ QApplicationPrivate::initializeWidgetFontHash();
+
initialize();
eventDispatcher->startingUp();
@@ -582,18 +577,6 @@ void QApplicationPrivate::init()
}
-void qt_init(QApplicationPrivate *priv, int type)
-{
- Q_UNUSED(priv);
- Q_UNUSED(type);
-
- QColormap::initialize();
-
- qt_init_tooltip_palette();
-
- QApplicationPrivate::initializeWidgetFontHash();
-}
-
void qt_init_tooltip_palette()
{
#ifndef QT_NO_TOOLTIP
@@ -659,7 +642,7 @@ void QApplicationPrivate::initializeWidgetPaletteHash()
QPlatformTheme *platformTheme = QGuiApplicationPrivate::platformTheme();
if (!platformTheme)
return;
- qt_app_palettes_hash()->clear();
+ app_palettes()->clear();
setPossiblePalette(platformTheme->palette(QPlatformTheme::ToolButtonPalette), "QToolButton");
setPossiblePalette(platformTheme->palette(QPlatformTheme::ButtonPalette), "QAbstractButton");
@@ -683,7 +666,7 @@ void QApplicationPrivate::initializeWidgetFontHash()
const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme();
if (!theme)
return;
- FontHash *fontHash = qt_app_fonts_hash();
+ FontHash *fontHash = app_fonts();
fontHash->clear();
if (const QFont *font = theme->font(QPlatformTheme::MenuFont))
@@ -1162,9 +1145,7 @@ void QApplication::setStyle(QStyle *style)
} else if (QApplicationPrivate::sys_pal) {
clearSystemPalette();
initSystemPalette();
- QApplicationPrivate::initializeWidgetPaletteHash();
QApplicationPrivate::initializeWidgetFontHash();
- QApplicationPrivate::setPalette_helper(*QApplicationPrivate::sys_pal, /*className=*/0, /*clearWidgetPaletteHash=*/false);
} else if (!QApplicationPrivate::sys_pal) {
// Initialize the sys_pal if it hasn't happened yet...
QApplicationPrivate::setSystemPalette(QApplicationPrivate::app_style->standardPalette());
@@ -1462,28 +1443,10 @@ void QApplication::setPalette(const QPalette &palette, const char* className)
void QApplicationPrivate::setSystemPalette(const QPalette &pal)
{
- QPalette adjusted;
-
-#if 0
- // adjust the system palette to avoid dithering
- QColormap cmap = QColormap::instance();
- if (cmap.depths() > 4 && cmap.depths() < 24) {
- for (int g = 0; g < QPalette::NColorGroups; g++)
- for (int i = 0; i < QPalette::NColorRoles; i++) {
- QColor color = pal.color((QPalette::ColorGroup)g, (QPalette::ColorRole)i);
- color = cmap.colorAt(cmap.pixel(color));
- adjusted.setColor((QPalette::ColorGroup)g, (QPalette::ColorRole) i, color);
- }
- }
-#else
- adjusted = pal;
-#endif
-
if (!sys_pal)
- sys_pal = new QPalette(adjusted);
+ sys_pal = new QPalette(pal);
else
- *sys_pal = adjusted;
-
+ *sys_pal = pal;
if (!QApplicationPrivate::set_pal)
QApplication::setPalette(*sys_pal);
diff --git a/src/widgets/kernel/qdesktopwidget.cpp b/src/widgets/kernel/qdesktopwidget.cpp
index d17c7eb36c..ac0cfcf2f5 100644
--- a/src/widgets/kernel/qdesktopwidget.cpp
+++ b/src/widgets/kernel/qdesktopwidget.cpp
@@ -321,20 +321,12 @@ int QDesktopWidgetPrivate::screenNumber(const QWidget *w)
if (screens.isEmpty()) // This should never happen
return primaryScreen();
- const QWindow *winHandle = w->windowHandle();
- if (!winHandle) {
- if (const QWidget *nativeParent = w->nativeParentWidget())
- winHandle = nativeParent->windowHandle();
- }
-
// If there is more than one virtual desktop
if (screens.count() != screens.constFirst()->virtualSiblings().count()) {
// Find the root widget, get a QScreen from it and use the
// virtual siblings for checking the window position.
- if (winHandle) {
- if (const QScreen *winScreen = winHandle->screen())
- screens = winScreen->virtualSiblings();
- }
+ if (const QScreen *winScreen = qt_widget_private(const_cast<QWidget *>(w))->associatedScreen())
+ screens = winScreen->virtualSiblings();
}
// Get the screen number from window position using screen geometry
diff --git a/src/widgets/kernel/qopenglwidget.cpp b/src/widgets/kernel/qopenglwidget.cpp
index 374ea53726..ae7729b49b 100644
--- a/src/widgets/kernel/qopenglwidget.cpp
+++ b/src/widgets/kernel/qopenglwidget.cpp
@@ -1333,11 +1333,8 @@ int QOpenGLWidget::metric(QPaintDevice::PaintDeviceMetric metric) const
if (d->inBackingStorePaint)
return QWidget::metric(metric);
- QWidget *tlw = window();
- QWindow *window = tlw ? tlw->windowHandle() : 0;
- QScreen *screen = tlw && tlw->windowHandle() ? tlw->windowHandle()->screen() : 0;
- if (!screen && QGuiApplication::primaryScreen())
- screen = QGuiApplication::primaryScreen();
+ auto window = d->windowHandle(QWidgetPrivate::WindowHandleMode::TopLevel);
+ QScreen *screen = window ? window->screen() : QGuiApplication::primaryScreen();
const float dpmx = qt_defaultDpiX() * 100. / 2.54;
const float dpmy = qt_defaultDpiY() * 100. / 2.54;
diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp
index 8f927e8bee..2176c612d0 100644
--- a/src/widgets/kernel/qwidget.cpp
+++ b/src/widgets/kernel/qwidget.cpp
@@ -1,4 +1,4 @@
-/****************************************************************************
+/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Copyright (C) 2016 Intel Corporation.
@@ -79,6 +79,7 @@
#include "private/qstylesheetstyle_p.h"
#include "private/qstyle_p.h"
#include "qfileinfo.h"
+#include "qscopeguard.h"
#include <QtGui/private/qhighdpiscaling_p.h>
#include <QtGui/qinputmethod.h>
#include <QtGui/qopenglcontext.h>
@@ -1123,6 +1124,8 @@ void QWidgetPrivate::adjustFlags(Qt::WindowFlags &flags, QWidget *w)
void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f)
{
Q_Q(QWidget);
+ Q_ASSERT_X(q != parentWidget, Q_FUNC_INFO, "Cannot parent a QWidget to itself");
+
if (Q_UNLIKELY(!qobject_cast<QApplication *>(QCoreApplication::instance())))
qFatal("QWidget: Cannot create a QWidget without QApplication");
@@ -1249,6 +1252,33 @@ void QWidgetPrivate::createRecursively()
}
}
+QWindow *QWidgetPrivate::windowHandle(WindowHandleMode mode) const
+{
+ if (mode == WindowHandleMode::Direct || mode == WindowHandleMode::Closest) {
+ if (QTLWExtra *x = maybeTopData())
+ return x->window;
+ }
+ if (mode == WindowHandleMode::Closest) {
+ if (auto nativeParent = q_func()->nativeParentWidget()) {
+ if (auto window = nativeParent->windowHandle())
+ return window;
+ }
+ }
+ if (mode == WindowHandleMode::TopLevel || mode == WindowHandleMode::Closest) {
+ if (auto topLevel = q_func()->topLevelWidget()) {
+ if (auto window = topLevel ->windowHandle())
+ return window;
+ }
+ }
+ return nullptr;
+}
+
+QScreen *QWidgetPrivate::associatedScreen() const
+{
+ if (auto window = windowHandle(WindowHandleMode::Closest))
+ return window->screen();
+ return nullptr;
+}
// ### fixme: Qt 6: Remove parameter window from QWidget::create()
@@ -2587,14 +2617,27 @@ bool QWidgetPrivate::setScreenForPoint(const QPoint &pos)
Q_Q(QWidget);
if (!q->isWindow())
return false;
- // Find the screen for pos and make the widget undertand it is on that screen.
+ // Find the screen for pos and make the widget understand it is on that screen.
+ return setScreen(QGuiApplication::screenAt(pos));
+}
+
+/*!
+\internal
+Ensures that the widget's QWindow is set to be on the given \a screen.
+Returns true if the screen was changed.
+*/
+
+bool QWidgetPrivate::setScreen(QScreen *screen)
+{
+ Q_Q(QWidget);
+ if (!screen || !q->isWindow())
+ return false;
const QScreen *currentScreen = windowHandle() ? windowHandle()->screen() : nullptr;
- QScreen *actualScreen = QGuiApplication::screenAt(pos);
- if (actualScreen && currentScreen != actualScreen) {
+ if (currentScreen != screen) {
if (!windowHandle()) // Try to create a window handle if not created.
createWinId();
if (windowHandle())
- windowHandle()->setScreen(actualScreen);
+ windowHandle()->setScreen(screen);
return true;
}
return false;
@@ -6995,37 +7038,41 @@ void QWidget::setTabOrder(QWidget* first, QWidget *second)
lastFocusChild = focusNext;
}
};
+ auto setPrev = [](QWidget *w, QWidget *prev)
+ {
+ w->d_func()->focus_prev = prev;
+ };
+ auto setNext = [](QWidget *w, QWidget *next)
+ {
+ w->d_func()->focus_next = next;
+ };
- QWidget *lastFocusChildOfFirst, *lastFocusChildOfSecond;
- determineLastFocusChild(first, lastFocusChildOfFirst);
+ // remove the second widget from the chain
+ QWidget *lastFocusChildOfSecond;
determineLastFocusChild(second, lastFocusChildOfSecond);
-
- // If the tab order is already correct, exit early
- if (lastFocusChildOfFirst == second ||
- lastFocusChildOfFirst->d_func()->focus_next == second) {
- return;
+ {
+ QWidget *oldPrev = second->d_func()->focus_prev;
+ QWidget *prevWithFocus = oldPrev;
+ while (prevWithFocus->focusPolicy() == Qt::NoFocus)
+ prevWithFocus = prevWithFocus->d_func()->focus_prev;
+ // only widgets between first and second -> all is fine
+ if (prevWithFocus == first)
+ return;
+ QWidget *oldNext = lastFocusChildOfSecond->d_func()->focus_next;
+ setPrev(oldNext, oldPrev);
+ setNext(oldPrev, oldNext);
}
- // Note that we need to handle two different sections in the tab chain; The section
- // that 'first' belongs to (firstSection), where we are about to insert 'second', and
- // the section that 'second' used be a part of (secondSection). When we pull 'second'
- // out of the second section and insert it into the first, we also need to ensure
- // that we leave the second section in a connected state.
- QWidget *firstChainOldSecond = lastFocusChildOfFirst->d_func()->focus_next;
- QWidget *secondChainNewFirst = second->d_func()->focus_prev;
- QWidget *secondChainNewSecond = lastFocusChildOfSecond->d_func()->focus_next;
-
- // Insert 'second' after 'first'
- lastFocusChildOfFirst->d_func()->focus_next = second;
- second->d_func()->focus_prev = lastFocusChildOfFirst;
-
- // The widget that used to be 'second' in the first section, should now become 'third'
- lastFocusChildOfSecond->d_func()->focus_next = firstChainOldSecond;
- firstChainOldSecond->d_func()->focus_prev = lastFocusChildOfSecond;
-
- // Repair the second section after we pulled 'second' out of it
- secondChainNewFirst->d_func()->focus_next = secondChainNewSecond;
- secondChainNewSecond->d_func()->focus_prev = secondChainNewFirst;
+ // insert the second widget into the chain
+ QWidget *lastFocusChildOfFirst;
+ determineLastFocusChild(first, lastFocusChildOfFirst);
+ {
+ QWidget *oldNext = lastFocusChildOfFirst->d_func()->focus_next;
+ setPrev(second, lastFocusChildOfFirst);
+ setNext(lastFocusChildOfFirst, second);
+ setPrev(oldNext, lastFocusChildOfSecond);
+ setNext(lastFocusChildOfSecond, oldNext);
+ }
}
/*!\internal
@@ -8100,7 +8147,7 @@ void QWidgetPrivate::show_sys()
{
Q_Q(QWidget);
- QWidgetWindow *window = windowHandle();
+ auto window = qobject_cast<QWidgetWindow *>(windowHandle());
if (q->testAttribute(Qt::WA_DontShowOnScreen)) {
invalidateBackingStore(q->rect());
@@ -8239,7 +8286,7 @@ void QWidgetPrivate::hide_sys()
{
Q_Q(QWidget);
- QWidgetWindow *window = windowHandle();
+ auto window = qobject_cast<QWidgetWindow *>(windowHandle());
if (q->testAttribute(Qt::WA_DontShowOnScreen)) {
q->setAttribute(Qt::WA_Mapped, false);
@@ -10677,6 +10724,22 @@ static void sendWindowChangeToTextureChildrenRecursively(QWidget *widget)
void QWidget::setParent(QWidget *parent, Qt::WindowFlags f)
{
Q_D(QWidget);
+ Q_ASSERT_X(this != parent, Q_FUNC_INFO, "Cannot parent a QWidget to itself");
+#ifdef QT_DEBUG
+ const auto checkForParentChildLoops = qScopeGuard([&](){
+ int depth = 0;
+ auto p = parentWidget();
+ while (p) {
+ if (++depth == QObjectPrivate::CheckForParentChildLoopsWarnDepth) {
+ qWarning("QWidget %p (class: '%s', object name: '%s') may have a loop in its parent-child chain; "
+ "this is undefined behavior",
+ this, metaObject()->className(), qPrintable(objectName()));
+ }
+ p = p->parentWidget();
+ }
+ });
+#endif
+
bool resized = testAttribute(Qt::WA_Resized);
bool wasCreated = testAttribute(Qt::WA_WState_Created);
QWidget *oldtlw = window();
diff --git a/src/widgets/kernel/qwidget_p.h b/src/widgets/kernel/qwidget_p.h
index c073b8fb03..99ec38e050 100644
--- a/src/widgets/kernel/qwidget_p.h
+++ b/src/widgets/kernel/qwidget_p.h
@@ -181,6 +181,7 @@ struct QTLWExtra {
QRect frameStrut;
QRect normalGeometry; // used by showMin/maximized/FullScreen
Qt::WindowFlags savedFlags; // Save widget flags while showing fullscreen
+ // ### TODO replace initialScreenIndex with QScreen *, in case the screens change at runtime
int initialScreenIndex; // Screen number when passing a QDesktop[Screen]Widget as parent.
QVector<QPlatformTextureList *> widgetTextures;
@@ -342,7 +343,15 @@ public:
QPainter *sharedPainter() const;
void setSharedPainter(QPainter *painter);
QWidgetBackingStore *maybeBackingStore() const;
- QWidgetWindow *windowHandle() const;
+
+ enum class WindowHandleMode {
+ Direct,
+ Closest,
+ TopLevel
+ };
+ QWindow *windowHandle(WindowHandleMode mode = WindowHandleMode::Direct) const;
+
+ QScreen *associatedScreen() const;
template <typename T>
void repaint(T t);
@@ -356,6 +365,7 @@ public:
void createWinId();
bool setScreenForPoint(const QPoint &pos);
+ bool setScreen(QScreen *screen);
void createTLExtra();
void createExtra();
@@ -1014,13 +1024,6 @@ inline QWidgetBackingStore *QWidgetPrivate::maybeBackingStore() const
return x ? x->backingStoreTracker.data() : nullptr;
}
-inline QWidgetWindow *QWidgetPrivate::windowHandle() const
-{
- if (QTLWExtra *x = maybeTopData())
- return x->window;
- return nullptr;
-}
-
QT_END_NAMESPACE
#endif // QWIDGET_P_H
diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp
index 70b305326c..a5d9eee49d 100644
--- a/src/widgets/kernel/qwidgetwindow.cpp
+++ b/src/widgets/kernel/qwidgetwindow.cpp
@@ -592,10 +592,7 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
w->window()->raise();
}
- QWindow *win = w->windowHandle();
- if (!win)
- win = w->nativeParentWidget()->windowHandle();
- if (win) {
+ if (auto win = qt_widget_private(w)->windowHandle(QWidgetPrivate::WindowHandleMode::Closest)) {
const QRect globalGeometry = win->isTopLevel()
? win->geometry()
: QRect(win->mapToGlobal(QPoint(0, 0)), win->size());
diff --git a/src/widgets/styles/qfusionstyle.cpp b/src/widgets/styles/qfusionstyle.cpp
index f3961b2a99..34cc3c93db 100644
--- a/src/widgets/styles/qfusionstyle.cpp
+++ b/src/widgets/styles/qfusionstyle.cpp
@@ -2769,8 +2769,16 @@ void QFusionStyle::drawComplexControl(ComplexControl control, const QStyleOption
buttonOption.state &= ~State_MouseOver;
}
- if (comboBox->frame)
+ if (comboBox->frame) {
+ cachePainter.save();
+ cachePainter.setRenderHint(QPainter::Antialiasing, true);
+ cachePainter.translate(0.5, 0.5);
+ cachePainter.setPen(Qt::NoPen);
+ cachePainter.setBrush(buttonOption.palette.base());
+ cachePainter.drawRoundedRect(rect.adjusted(0, 0, -1, -1), 2, 2);
+ cachePainter.restore();
proxy()->drawPrimitive(PE_FrameLineEdit, &buttonOption, &cachePainter, widget);
+ }
// Draw button clipped
cachePainter.save();
diff --git a/src/widgets/styles/qstylesheetstyle.cpp b/src/widgets/styles/qstylesheetstyle.cpp
index 025e1e0044..c1f498cd0c 100644
--- a/src/widgets/styles/qstylesheetstyle.cpp
+++ b/src/widgets/styles/qstylesheetstyle.cpp
@@ -3706,17 +3706,6 @@ void QStyleSheetStyle::drawControl(ControlElement ce, const QStyleOption *opt, Q
bool dis = !(opt->state & QStyle::State_Enabled),
act = opt->state & QStyle::State_Selected;
- int checkableOffset = 0;
- if (checkable) {
- QRenderRule subSubRule = renderRule(w, opt, PseudoElement_MenuCheckMark);
- QStyleOptionMenuItem newMi = mi;
- newMi.rect = positionRect(w, subRule, subSubRule, PseudoElement_MenuCheckMark, opt->rect, opt->direction);
- // align with icons if there are some
- checkableOffset = std::max(m->maxIconWidth, newMi.rect.width());
- if (subSubRule.hasDrawable() || checked)
- drawPrimitive(PE_IndicatorMenuCheckMark, &newMi, p, w);
- }
-
if (!mi.icon.isNull()) {
QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal;
if (act && !dis)
@@ -3733,24 +3722,28 @@ void QStyleSheetStyle::drawControl(ControlElement ce, const QStyleOption *opt, Q
}
QRect iconRect = positionRect(w, subRule, iconRule, PseudoElement_MenuIcon, opt->rect, opt->direction);
if (opt->direction == Qt::LeftToRight)
- iconRect.moveLeft(iconRect.left() + checkableOffset);
+ iconRect.moveLeft(iconRect.left());
else
- iconRect.moveRight(iconRect.right() - checkableOffset);
+ iconRect.moveRight(iconRect.right());
iconRule.drawRule(p, iconRect);
QRect pmr(0, 0, pixw, pixh);
pmr.moveCenter(iconRect.center());
p->drawPixmap(pmr.topLeft(), pixmap);
+ } else if (checkable) {
+ QRenderRule subSubRule = renderRule(w, opt, PseudoElement_MenuCheckMark);
+ if (subSubRule.hasDrawable() || checked) {
+ QStyleOptionMenuItem newMi = mi;
+ if (!dis)
+ newMi.state |= State_Enabled;
+ if (act)
+ newMi.state |= State_On;
+ newMi.rect = positionRect(w, subRule, subSubRule, PseudoElement_MenuCheckMark, opt->rect, opt->direction);
+ drawPrimitive(PE_IndicatorMenuCheckMark, &newMi, p, w);
+ }
}
- int textOffset = 0;
- // padding overrules it all
- if (!subRule.hasBox() || subRule.box()->paddings[LeftEdge] == 0) {
- textOffset = checkableOffset;
- if (!m->icon.isNull() || !checkable)
- textOffset += m->maxIconWidth;
- }
QRect textRect = subRule.contentsRect(opt->rect);
- textRect.setLeft(textRect.left() + textOffset);
+ textRect.setLeft(textRect.left() + m->maxIconWidth);
textRect.setWidth(textRect.width() - mi.tabWidth);
const QRect vTextRect = visualRect(opt->direction, m->rect, textRect);
@@ -5106,27 +5099,20 @@ QSize QStyleSheetStyle::sizeFromContents(ContentsType ct, const QStyleOption *op
if (mi->text.contains(QLatin1Char('\t')))
sz.rwidth() += 12; //as in QCommonStyle
bool checkable = mi->checkType != QStyleOptionMenuItem::NotCheckable;
- int checkableWidth = 0;
- if (checkable) {
+ if (!mi->icon.isNull()) {
+ const int pmSmall = pixelMetric(PM_SmallIconSize);
+ const QSize pmSize = mi->icon.actualSize(QSize(pmSmall, pmSmall));
+ sz.rwidth() += pmSize.width() + 4;
+ } else if (checkable) {
QRenderRule subSubRule = renderRule(w, opt, PseudoElement_MenuCheckMark);
QRect checkmarkRect = positionRect(w, subRule, subSubRule, PseudoElement_MenuCheckMark, opt->rect, opt->direction);
- checkableWidth = std::max(mi->maxIconWidth, checkmarkRect.width());
- }
- if (!mi->icon.isNull()) {
- QPixmap pixmap = mi->icon.pixmap(pixelMetric(PM_SmallIconSize));
- sz.rwidth() += pixmap.width();
+ sz.rwidth() += std::max(mi->maxIconWidth, checkmarkRect.width()) + 4;
}
if (subRule.hasFont) {
QFontMetrics fm(subRule.font);
const QRect r = fm.boundingRect(QRect(), Qt::TextSingleLine | Qt::TextShowMnemonic, mi->text);
sz = sz.expandedTo(r.size());
}
- // padding overrules it all
- if (!subRule.hasBox() || subRule.box()->paddings[LeftEdge] == 0) {
- sz.rwidth() += checkableWidth;
- if (!mi->icon.isNull() || !checkable)
- sz.rwidth() += mi->maxIconWidth;
- }
return subRule.boxSize(subRule.adjustSize(sz));
}
}
diff --git a/src/widgets/styles/qwindowsstyle.cpp b/src/widgets/styles/qwindowsstyle.cpp
index 7b5da9bfa8..3c0478b84e 100644
--- a/src/widgets/styles/qwindowsstyle.cpp
+++ b/src/widgets/styles/qwindowsstyle.cpp
@@ -380,23 +380,12 @@ int QWindowsStylePrivate::fixedPixelMetric(QStyle::PixelMetric pm)
return QWindowsStylePrivate::InvalidMetric;
}
-static QWindow *windowOf(const QWidget *w)
+static QScreen *screenOf(const QWidget *w)
{
- QWindow *result = nullptr;
if (w) {
- result = w->windowHandle();
- if (!result) {
- if (const QWidget *np = w->nativeParentWidget())
- result = np->windowHandle();
- }
+ if (auto screen = qt_widget_private(const_cast<QWidget *>(w))->associatedScreen())
+ return screen;
}
- return result;
-}
-
-static QScreen *screenOf(const QWidget *w)
-{
- if (const QWindow *window = windowOf(w))
- return window->screen();
return QGuiApplication::primaryScreen();
}
diff --git a/src/widgets/widgets/qcombobox.cpp b/src/widgets/widgets/qcombobox.cpp
index 07c55e4db6..a052f2df79 100644
--- a/src/widgets/widgets/qcombobox.cpp
+++ b/src/widgets/widgets/qcombobox.cpp
@@ -1521,7 +1521,7 @@ int QComboBox::maxCount() const
/*!
\obsolete
- Use setCompleter() instead.
+ Use completer() instead.
*/
bool QComboBox::autoCompletion() const
{
@@ -1881,12 +1881,11 @@ void QComboBox::setLineEdit(QLineEdit *edit)
d->updateFocusPolicy();
d->lineEdit->setFocusProxy(this);
d->lineEdit->setAttribute(Qt::WA_MacShowFocusRect, false);
+#if QT_DEPRECATED_SINCE(5, 13)
#if QT_CONFIG(completer)
setAutoCompletion(d->autoCompletion);
-#endif
#ifdef QT_KEYPAD_NAVIGATION
-#if QT_CONFIG(completer)
if (QApplication::keypadNavigationEnabled()) {
// Editable combo boxes will have a completer that is set to UnfilteredPopupCompletion.
// This means that when the user enters edit mode they are immediately presented with a
@@ -1899,6 +1898,7 @@ void QComboBox::setLineEdit(QLineEdit *edit)
}
#endif
#endif
+#endif
setAttribute(Qt::WA_InputMethodEnabled);
d->updateLayoutDirection();
@@ -2838,19 +2838,15 @@ void QComboBox::showPopup()
bool startTimer = !container->isVisible();
container->raise();
container->create();
- QWindow *containerWindow = container->window()->windowHandle();
- if (containerWindow) {
- QWindow *win = window()->windowHandle();
- if (win) {
- QScreen *currentScreen = win->screen();
- if (currentScreen && !currentScreen->virtualSiblings().contains(containerWindow->screen())) {
- containerWindow->setScreen(currentScreen);
-
- // This seems to workaround an issue in xcb+multi GPU+multiscreen
- // environment where the window might not always show up when screen
- // is changed.
- container->hide();
- }
+ if (QWindow *containerWindow = qt_widget_private(container)->windowHandle(QWidgetPrivate::WindowHandleMode::TopLevel)) {
+ QScreen *currentScreen = d->associatedScreen();
+ if (currentScreen && !currentScreen->virtualSiblings().contains(containerWindow->screen())) {
+ containerWindow->setScreen(currentScreen);
+
+ // This seems to workaround an issue in xcb+multi GPU+multiscreen
+ // environment where the window might not always show up when screen
+ // is changed.
+ container->hide();
}
}
container->show();
diff --git a/src/widgets/widgets/qcombobox.h b/src/widgets/widgets/qcombobox.h
index 37b155774d..3b48b151a1 100644
--- a/src/widgets/widgets/qcombobox.h
+++ b/src/widgets/widgets/qcombobox.h
@@ -96,10 +96,13 @@ public:
#if QT_CONFIG(completer)
#if QT_DEPRECATED_SINCE(5, 13)
+ QT_DEPRECATED_X("Use completer() instead.")
bool autoCompletion() const;
+ QT_DEPRECATED_X("Use setCompleter() instead.")
void setAutoCompletion(bool enable);
-
+ QT_DEPRECATED_X("Use completer()->caseSensitivity() instead.")
Qt::CaseSensitivity autoCompletionCaseSensitivity() const;
+ QT_DEPRECATED_X("Use completer()->setCaseSensitivity() instead.")
void setAutoCompletionCaseSensitivity(Qt::CaseSensitivity sensitivity);
#endif
#endif
diff --git a/src/widgets/widgets/qdatetimeedit.cpp b/src/widgets/widgets/qdatetimeedit.cpp
index 3f41fdeb59..642f35c638 100644
--- a/src/widgets/widgets/qdatetimeedit.cpp
+++ b/src/widgets/widgets/qdatetimeedit.cpp
@@ -1529,7 +1529,7 @@ void QDateTimeEdit::mousePressEvent(QMouseEvent *event)
QTimeEdit::QTimeEdit(QWidget *parent)
: QDateTimeEdit(QDATETIMEEDIT_TIME_MIN, QVariant::Time, parent)
{
- connect(this, SIGNAL(timeChanged(QTime)), SIGNAL(userTimeChanged(QTime)));
+ connect(this, &QTimeEdit::timeChanged, this, &QTimeEdit::userTimeChanged);
}
/*!
@@ -1540,6 +1540,7 @@ QTimeEdit::QTimeEdit(QWidget *parent)
QTimeEdit::QTimeEdit(const QTime &time, QWidget *parent)
: QDateTimeEdit(time, QVariant::Time, parent)
{
+ connect(this, &QTimeEdit::timeChanged, this, &QTimeEdit::userTimeChanged);
}
/*!
@@ -1598,7 +1599,7 @@ QTimeEdit::~QTimeEdit()
QDateEdit::QDateEdit(QWidget *parent)
: QDateTimeEdit(QDATETIMEEDIT_DATE_INITIAL, QVariant::Date, parent)
{
- connect(this, SIGNAL(dateChanged(QDate)), SIGNAL(userDateChanged(QDate)));
+ connect(this, &QDateEdit::dateChanged, this, &QDateEdit::userDateChanged);
}
/*!
@@ -1609,6 +1610,7 @@ QDateEdit::QDateEdit(QWidget *parent)
QDateEdit::QDateEdit(const QDate &date, QWidget *parent)
: QDateTimeEdit(date, QVariant::Date, parent)
{
+ connect(this, &QDateEdit::dateChanged, this, &QDateEdit::userDateChanged);
}
/*!
diff --git a/src/widgets/widgets/qdockwidget.cpp b/src/widgets/widgets/qdockwidget.cpp
index f98e0e44db..b8b6c12bf3 100644
--- a/src/widgets/widgets/qdockwidget.cpp
+++ b/src/widgets/widgets/qdockwidget.cpp
@@ -1535,10 +1535,10 @@ bool QDockWidget::event(QEvent *event)
d->toggleViewAction->setChecked(true);
QPoint parentTopLeft(0, 0);
if (isWindow()) {
- if (const QWindow *window = windowHandle())
- parentTopLeft = window->screen()->availableVirtualGeometry().topLeft();
- else
- parentTopLeft = QGuiApplication::primaryScreen()->availableVirtualGeometry().topLeft();
+ const QScreen *screen = d->associatedScreen();
+ parentTopLeft = screen
+ ? screen->availableVirtualGeometry().topLeft()
+ : QGuiApplication::primaryScreen()->availableVirtualGeometry().topLeft();
}
emit visibilityChanged(geometry().right() >= parentTopLeft.x() && geometry().bottom() >= parentTopLeft.y());
}
diff --git a/src/widgets/widgets/qlineedit_p.cpp b/src/widgets/widgets/qlineedit_p.cpp
index 21e70db0ac..7d580e50a5 100644
--- a/src/widgets/widgets/qlineedit_p.cpp
+++ b/src/widgets/widgets/qlineedit_p.cpp
@@ -355,9 +355,7 @@ QLineEditPrivate *QLineEditIconButton::lineEditPrivate() const
void QLineEditIconButton::paintEvent(QPaintEvent *)
{
QPainter painter(this);
- QWindow *window = nullptr;
- if (const QWidget *nativeParent = nativeParentWidget())
- window = nativeParent->windowHandle();
+ QWindow *window = qt_widget_private(this)->windowHandle(QWidgetPrivate::WindowHandleMode::Closest);
QIcon::Mode state = QIcon::Disabled;
if (isEnabled())
state = isDown() ? QIcon::Active : QIcon::Normal;
diff --git a/src/widgets/widgets/qmainwindowlayout.cpp b/src/widgets/widgets/qmainwindowlayout.cpp
index f54835f23b..b8f997b782 100644
--- a/src/widgets/widgets/qmainwindowlayout.cpp
+++ b/src/widgets/widgets/qmainwindowlayout.cpp
@@ -2584,9 +2584,9 @@ void QMainWindowLayout::hover(QLayoutItem *widgetItem, const QPoint &mousePos)
}
}
for (QWidget *w : candidates) {
- QWindow *handle1 = widget->windowHandle();
- QWindow *handle2 = w->windowHandle();
- if (handle1 && handle2 && handle1->screen() != handle2->screen())
+ const QScreen *screen1 = qt_widget_private(widget)->associatedScreen();
+ const QScreen *screen2 = qt_widget_private(w)->associatedScreen();
+ if (screen1 && screen2 && screen1 != screen2)
continue;
if (!w->geometry().contains(mousePos))
continue;
diff --git a/src/widgets/widgets/qmdisubwindow.cpp b/src/widgets/widgets/qmdisubwindow.cpp
index e25bc6de7a..685c5e159e 100644
--- a/src/widgets/widgets/qmdisubwindow.cpp
+++ b/src/widgets/widgets/qmdisubwindow.cpp
@@ -3141,8 +3141,6 @@ void QMdiSubWindow::paintEvent(QPaintEvent *paintEvent)
}
Q_D(QMdiSubWindow);
- if (isMaximized() && !d->drawTitleBarWhenMaximized())
- return;
if (d->resizeTimerId != -1) {
// Only update the style option rect and the window title.
@@ -3162,6 +3160,17 @@ void QMdiSubWindow::paintEvent(QPaintEvent *paintEvent)
}
QStylePainter painter(this);
+ QStyleOptionFrame frameOptions;
+ frameOptions.initFrom(this);
+ frameOptions.state.setFlag(QStyle::State_Active, d->isActive);
+ if (isMaximized() && !d->drawTitleBarWhenMaximized()) {
+ if (!autoFillBackground() && (!widget() || !qt_widget_private(widget())->isOpaque)) {
+ // make sure we paint all pixels of a maximized QMdiSubWindow if no-one else does
+ painter.drawPrimitive(QStyle::PE_FrameWindow, frameOptions);
+ }
+ return;
+ }
+
if (!d->windowTitle.isEmpty())
painter.setFont(d->font);
painter.drawComplexControl(QStyle::CC_TitleBar, d->cachedStyleOptions);
@@ -3169,10 +3178,7 @@ void QMdiSubWindow::paintEvent(QPaintEvent *paintEvent)
if (isMinimized() && !d->hasBorder(d->cachedStyleOptions))
return;
- QStyleOptionFrame frameOptions;
- frameOptions.initFrom(this);
frameOptions.lineWidth = style()->pixelMetric(QStyle::PM_MdiSubWindowFrameWidth, 0, this);
- frameOptions.state.setFlag(QStyle::State_Active, d->isActive);
// ### Ensure that we do not require setting the cliprect for 4.4
if (!isMinimized() && !d->hasBorder(d->cachedStyleOptions))
diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp
index 287be3e272..7b6a1b6da8 100644
--- a/src/widgets/widgets/qmenu.cpp
+++ b/src/widgets/widgets/qmenu.cpp
@@ -2331,8 +2331,18 @@ void QMenu::popup(const QPoint &p, QAction *atAction)
d->updateLayoutDirection();
// Ensure that we get correct sizeHints by placing this window on the correct screen.
- if (d->setScreenForPoint(p))
+ // However if the QMenu was constructed with a QDesktopScreenWidget as its parent,
+ // then initialScreenIndex was set, so we should respect that for the lifetime of this menu.
+ // Use d->popupScreen to remember, because initialScreenIndex will be reset after the first showing.
+ const int screenIndex = d->topData()->initialScreenIndex;
+ if (screenIndex >= 0)
+ d->popupScreen = screenIndex;
+ if (auto s = QGuiApplication::screens().value(d->popupScreen)) {
+ if (d->setScreen(s))
+ d->itemsDirty = true;
+ } else if (d->setScreenForPoint(p)) {
d->itemsDirty = true;
+ }
const bool contextMenu = d->isContextMenu();
if (d->lastContextMenu != contextMenu) {
diff --git a/src/widgets/widgets/qmenu_p.h b/src/widgets/widgets/qmenu_p.h
index ef152cd71b..efbbc099a1 100644
--- a/src/widgets/widgets/qmenu_p.h
+++ b/src/widgets/widgets/qmenu_p.h
@@ -515,6 +515,8 @@ public:
bool tearoffHighlighted : 1;
//menu fading/scrolling effects
bool doChildEffects : 1;
+
+ int popupScreen = -1;
};
QT_END_NAMESPACE
diff --git a/src/widgets/widgets/qmenubar.cpp b/src/widgets/widgets/qmenubar.cpp
index a53d7841f4..9a60f1477d 100644
--- a/src/widgets/widgets/qmenubar.cpp
+++ b/src/widgets/widgets/qmenubar.cpp
@@ -68,6 +68,7 @@
#include "qmenu_p.h"
#include "qmenubar_p.h"
+#include <private/qscreen_p.h>
#include "qdebug.h"
QT_BEGIN_NAMESPACE
@@ -322,11 +323,18 @@ void QMenuBarPrivate::popupAction(QAction *action, bool activateFirst)
QRect adjustedActionRect = actionRect(action);
QPoint pos(q->mapToGlobal(QPoint(adjustedActionRect.left(), adjustedActionRect.bottom() + 1)));
QSize popup_size = activeMenu->sizeHint();
-
//we put the popup menu on the screen containing the bottom-center of the action rect
- QRect screenRect = QDesktopWidgetPrivate::screenGeometry(pos + QPoint(adjustedActionRect.width() / 2, 0));
+ QScreen *popupScreen = q->window()->windowHandle()->screen();
+ QPoint bottomMiddlePos = pos + QPoint(adjustedActionRect.width() / 2, 0);
+ const auto &siblings = popupScreen->virtualSiblings();
+ for (QScreen *sibling : siblings) {
+ if (sibling->geometry().contains(bottomMiddlePos)) {
+ popupScreen = sibling;
+ break;
+ }
+ }
+ QRect screenRect = popupScreen->geometry();
pos = QPoint(qMax(pos.x(), screenRect.x()), qMax(pos.y(), screenRect.y()));
-
const bool fitUp = (pos.y() - popup_size.height() >= screenRect.top());
const bool fitDown = (pos.y() + popup_size.height() <= screenRect.bottom());
const bool rtl = q->isRightToLeft();
@@ -352,6 +360,7 @@ void QMenuBarPrivate::popupAction(QAction *action, bool activateFirst)
if(!defaultPopDown || (fitUp && !fitDown))
pos.setY(qMax(screenRect.y(), q->mapToGlobal(QPoint(0, adjustedActionRect.top()-popup_size.height())).y()));
+ QMenuPrivate::get(activeMenu)->topData()->initialScreenIndex = QGuiApplication::screens().indexOf(popupScreen);
activeMenu->popup(pos);
if(activateFirst)
activeMenu->d_func()->setFirstActionActive();
diff --git a/src/widgets/widgets/qplaintextedit.cpp b/src/widgets/widgets/qplaintextedit.cpp
index bc1ff78de0..4a875975a4 100644
--- a/src/widgets/widgets/qplaintextedit.cpp
+++ b/src/widgets/widgets/qplaintextedit.cpp
@@ -1957,6 +1957,7 @@ void QPlainTextEdit::paintEvent(QPaintEvent *e)
}
QAbstractTextDocumentLayout::PaintContext context = getPaintContext();
+ painter.setPen(context.palette.text().color());
while (block.isValid()) {
diff --git a/src/widgets/widgets/qtextbrowser.cpp b/src/widgets/widgets/qtextbrowser.cpp
index 8e06efc75e..bee1021950 100644
--- a/src/widgets/widgets/qtextbrowser.cpp
+++ b/src/widgets/widgets/qtextbrowser.cpp
@@ -86,6 +86,7 @@ public:
int hpos;
int vpos;
int focusIndicatorPosition, focusIndicatorAnchor;
+ QTextDocument::ResourceType type = QTextDocument::UnknownResource;
};
HistoryEntry history(int i) const
@@ -122,6 +123,8 @@ public:
bool openExternalLinks;
bool openLinks;
+ QTextDocument::ResourceType currentType;
+
#ifndef QT_NO_CURSOR
QCursor oldCursor;
#endif
@@ -137,7 +140,7 @@ public:
void _q_activateAnchor(const QString &href);
void _q_highlightLink(const QString &href);
- void setSource(const QUrl &url);
+ void setSource(const QUrl &url, QTextDocument::ResourceType type);
// re-imlemented from QTextEditPrivate
virtual QUrl resolveUrl(const QUrl &url) const override;
@@ -274,7 +277,7 @@ void QTextBrowserPrivate::_q_highlightLink(const QString &anchor)
}
}
-void QTextBrowserPrivate::setSource(const QUrl &url)
+void QTextBrowserPrivate::setSource(const QUrl &url, QTextDocument::ResourceType type)
{
Q_Q(QTextBrowser);
#ifndef QT_NO_CURSOR
@@ -291,14 +294,18 @@ void QTextBrowserPrivate::setSource(const QUrl &url)
currentUrlWithoutFragment.setFragment(QString());
QUrl newUrlWithoutFragment = currentURL.resolved(url);
newUrlWithoutFragment.setFragment(QString());
- QTextDocument::ResourceType type = QTextDocument::HtmlResource;
QString fileName = url.fileName();
+ if (type == QTextDocument::UnknownResource) {
#if QT_CONFIG(textmarkdownreader)
- if (fileName.endsWith(QLatin1String(".md")) ||
- fileName.endsWith(QLatin1String(".mkd")) ||
- fileName.endsWith(QLatin1String(".markdown")))
- type = QTextDocument::MarkdownResource;
+ if (fileName.endsWith(QLatin1String(".md")) ||
+ fileName.endsWith(QLatin1String(".mkd")) ||
+ fileName.endsWith(QLatin1String(".markdown")))
+ type = QTextDocument::MarkdownResource;
+ else
#endif
+ type = QTextDocument::HtmlResource;
+ }
+ currentType = type;
if (url.isValid()
&& (newUrlWithoutFragment != currentUrlWithoutFragment || forceLoadOnSourceChange)) {
@@ -574,6 +581,7 @@ QTextBrowserPrivate::HistoryEntry QTextBrowserPrivate::createHistoryEntry() cons
{
HistoryEntry entry;
entry.url = q_func()->source();
+ entry.type = q_func()->sourceType();
entry.title = q_func()->documentTitle();
entry.hpos = hbar->value();
entry.vpos = vbar->value();
@@ -590,7 +598,7 @@ QTextBrowserPrivate::HistoryEntry QTextBrowserPrivate::createHistoryEntry() cons
void QTextBrowserPrivate::restoreHistoryEntry(const HistoryEntry &entry)
{
- setSource(entry.url);
+ setSource(entry.url, entry.type);
hbar->setValue(entry.hpos);
vbar->setValue(entry.vpos);
if (entry.focusIndicatorAnchor != -1 && entry.focusIndicatorPosition != -1) {
@@ -732,7 +740,13 @@ QTextBrowser::~QTextBrowser()
document is displayed as a popup rather than as new document in
the browser window itself. Otherwise, the document is displayed
normally in the text browser with the text set to the contents of
- the named document with setHtml().
+ the named document with \l QTextDocument::setHtml() or
+ \l QTextDocument::setMarkdown(), depending on whether the filename ends
+ with any of the known Markdown file extensions.
+
+ If you would like to avoid automatic type detection
+ and specify the type explicitly, call setSource() rather than
+ setting this property.
By default, this property contains an empty URL.
*/
@@ -746,6 +760,23 @@ QUrl QTextBrowser::source() const
}
/*!
+ \property QTextBrowser::sourceType
+ \brief the type of the displayed document
+
+ This is QTextDocument::UnknownResource if no document is displayed or if
+ the type of the source is unknown. Otherwise it holds the type that was
+ detected, or the type that was specified when setSource() was called.
+*/
+QTextDocument::ResourceType QTextBrowser::sourceType() const
+{
+ Q_D(const QTextBrowser);
+ if (d->stack.isEmpty())
+ return QTextDocument::UnknownResource;
+ else
+ return d->stack.top().type;
+}
+
+/*!
\property QTextBrowser::searchPaths
\brief the search paths used by the text browser to find supporting
content
@@ -775,16 +806,46 @@ void QTextBrowser::reload()
Q_D(QTextBrowser);
QUrl s = d->currentURL;
d->currentURL = QUrl();
- setSource(s);
+ setSource(s, d->currentType);
}
+#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
void QTextBrowser::setSource(const QUrl &url)
{
+ setSource(url, QTextDocument::UnknownResource);
+}
+#endif
+
+/*!
+ Attempts to load the document at the given \a url with the specified \a type.
+
+ If \a type is \l {QTextDocument::ResourceType::UnknownResource}{UnknownResource}
+ (the default), the document type will be detected: that is, if the url ends
+ with an extension of \c{.md}, \c{.mkd} or \c{.markdown}, the document will be
+ loaded via \l QTextDocument::setMarkdown(); otherwise it will be loaded via
+ \l QTextDocument::setHtml(). This detection can be bypassed by specifying
+ the \a type explicitly.
+*/
+void QTextBrowser::setSource(const QUrl &url, QTextDocument::ResourceType type)
+{
+ doSetSource(url, type);
+}
+
+#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
+/*!
+ Attempts to load the document at the given \a url with the specified \a type.
+
+ setSource() calls doSetSource. In Qt 5, setSource(const QUrl &url) was virtual.
+ In Qt 6, doSetSource() is virtual instead, so that it can be overridden in subclasses.
+*/
+#endif
+void QTextBrowser::doSetSource(const QUrl &url, QTextDocument::ResourceType type)
+{
Q_D(QTextBrowser);
const QTextBrowserPrivate::HistoryEntry historyEntry = d->createHistoryEntry();
- d->setSource(url);
+ d->setSource(url, type);
if (!url.isValid())
return;
@@ -798,6 +859,7 @@ void QTextBrowser::setSource(const QUrl &url)
QTextBrowserPrivate::HistoryEntry entry;
entry.url = url;
+ entry.type = d->currentType;
entry.title = documentTitle();
entry.hpos = 0;
entry.vpos = 0;
diff --git a/src/widgets/widgets/qtextbrowser.h b/src/widgets/widgets/qtextbrowser.h
index ea81256f50..33e5b3980c 100644
--- a/src/widgets/widgets/qtextbrowser.h
+++ b/src/widgets/widgets/qtextbrowser.h
@@ -55,6 +55,7 @@ class Q_WIDGETS_EXPORT QTextBrowser : public QTextEdit
Q_OBJECT
Q_PROPERTY(QUrl source READ source WRITE setSource)
+ Q_PROPERTY(QTextDocument::ResourceType sourceType READ sourceType)
Q_OVERRIDE(bool modified SCRIPTABLE false)
Q_OVERRIDE(bool readOnly DESIGNABLE false SCRIPTABLE false)
Q_OVERRIDE(bool undoRedoEnabled DESIGNABLE false SCRIPTABLE false)
@@ -67,6 +68,7 @@ public:
virtual ~QTextBrowser();
QUrl source() const;
+ QTextDocument::ResourceType sourceType() const;
QStringList searchPaths() const;
void setSearchPaths(const QStringList &paths);
@@ -88,7 +90,12 @@ public:
void setOpenLinks(bool open);
public Q_SLOTS:
+#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
virtual void setSource(const QUrl &name);
+ void setSource(const QUrl &name, QTextDocument::ResourceType type);
+#else
+ void setSource(const QUrl &name, QTextDocument::ResourceType type = QTextDocument::UnknownResource);
+#endif
virtual void backward();
virtual void forward();
virtual void home();
@@ -112,6 +119,10 @@ protected:
virtual void focusOutEvent(QFocusEvent *ev) override;
virtual bool focusNextPrevChild(bool next) override;
virtual void paintEvent(QPaintEvent *e) override;
+#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
+ virtual
+#endif
+ void doSetSource(const QUrl &name, QTextDocument::ResourceType type = QTextDocument::UnknownResource);
private:
Q_DISABLE_COPY(QTextBrowser)
diff --git a/src/widgets/widgets/qwidgettextcontrol.cpp b/src/widgets/widgets/qwidgettextcontrol.cpp
index af3b03cd9e..cad3a64749 100644
--- a/src/widgets/widgets/qwidgettextcontrol.cpp
+++ b/src/widgets/widgets/qwidgettextcontrol.cpp
@@ -61,6 +61,9 @@
#include "private/qtextdocument_p.h"
#include "qtextlist.h"
#include "private/qwidgettextcontrol_p.h"
+#if QT_CONFIG(style_stylesheet)
+# include "private/qstylesheetstyle_p.h"
+#endif
#if QT_CONFIG(graphicsview)
#include "qgraphicssceneevent.h"
#endif
@@ -2479,7 +2482,7 @@ void QWidgetTextControl::setExtraSelections(const QList<QTextEdit::ExtraSelectio
QHash<int, int> hash;
for (int i = 0; i < d->extraSelections.count(); ++i) {
const QAbstractTextDocumentLayout::Selection &esel = d->extraSelections.at(i);
- hash.insertMulti(esel.cursor.anchor(), i);
+ hash.insert(esel.cursor.anchor(), i);
}
for (int i = 0; i < selections.count(); ++i) {
@@ -3256,6 +3259,15 @@ QAbstractTextDocumentLayout::PaintContext QWidgetTextControl::getPaintContext(QW
ctx.selections = d->extraSelections;
ctx.palette = d->palette;
+#if QT_CONFIG(style_stylesheet)
+ if (widget) {
+ if (auto cssStyle = qt_styleSheet(widget->style())) {
+ QStyleOption option;
+ option.initFrom(widget);
+ cssStyle->styleSheetPalette(widget, &option, &ctx.palette);
+ }
+ }
+#endif // style_stylesheet
if (d->cursorOn && d->isEnabled) {
if (d->hideCursor)
ctx.cursorPosition = -1;
diff --git a/src/xml/doc/src/dontdocument.qdoc b/src/xml/doc/src/dontdocument.qdoc
new file mode 100644
index 0000000000..0193ace4b4
--- /dev/null
+++ b/src/xml/doc/src/dontdocument.qdoc
@@ -0,0 +1,30 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \dontdocument (QTypeInfo)
+*/
diff --git a/src/xml/dom/qdom.cpp b/src/xml/dom/qdom.cpp
index 6498d53b96..8d232237bf 100644
--- a/src/xml/dom/qdom.cpp
+++ b/src/xml/dom/qdom.cpp
@@ -136,7 +136,7 @@ public:
class QDomNodePrivate
{
public:
- QDomNodePrivate(QDomDocumentPrivate*, QDomNodePrivate* parent = 0);
+ QDomNodePrivate(QDomDocumentPrivate*, QDomNodePrivate* parent = nullptr);
QDomNodePrivate(QDomNodePrivate* n, bool deep);
virtual ~QDomNodePrivate();
@@ -159,11 +159,11 @@ public:
virtual void normalize();
virtual void clear();
- inline QDomNodePrivate* parent() const { return hasParent ? ownerNode : 0; }
+ inline QDomNodePrivate* parent() const { return hasParent ? ownerNode : nullptr; }
inline void setParent(QDomNodePrivate *p) { ownerNode = p; hasParent = true; }
void setNoParent() {
- ownerNode = hasParent ? (QDomNodePrivate*)ownerDocument() : 0;
+ ownerNode = hasParent ? (QDomNodePrivate*)ownerDocument() : nullptr;
hasParent = false;
}
@@ -289,7 +289,7 @@ public:
class QDomDocumentTypePrivate : public QDomNodePrivate
{
public:
- QDomDocumentTypePrivate(QDomDocumentPrivate*, QDomNodePrivate* parent = 0);
+ QDomDocumentTypePrivate(QDomDocumentPrivate*, QDomNodePrivate* parent = nullptr);
QDomDocumentTypePrivate(QDomDocumentTypePrivate* n, bool deep);
~QDomDocumentTypePrivate();
void init();
@@ -317,7 +317,7 @@ public:
class QDomDocumentFragmentPrivate : public QDomNodePrivate
{
public:
- QDomDocumentFragmentPrivate(QDomDocumentPrivate*, QDomNodePrivate* parent = 0);
+ QDomDocumentFragmentPrivate(QDomDocumentPrivate*, QDomNodePrivate* parent = nullptr);
QDomDocumentFragmentPrivate(QDomNodePrivate* n, bool deep);
// Reimplemented from QDomNodePrivate
@@ -907,7 +907,7 @@ QDomImplementationPrivate* QDomImplementationPrivate::clone()
*/
QDomImplementation::QDomImplementation()
{
- impl = 0;
+ impl = nullptr;
}
/*!
@@ -1036,7 +1036,7 @@ QDomDocumentType QDomImplementation::createDocumentType(const QString& qName, co
if (!ok)
return QDomDocumentType();
- QDomDocumentTypePrivate *dt = new QDomDocumentTypePrivate(0);
+ QDomDocumentTypePrivate *dt = new QDomDocumentTypePrivate(nullptr);
dt->name = fixedName;
if (systemId.isNull()) {
dt->publicId.clear();
@@ -1070,7 +1070,7 @@ QDomDocument QDomImplementation::createDocument(const QString& nsURI, const QStr
*/
bool QDomImplementation::isNull()
{
- return (impl == 0);
+ return (impl == nullptr);
}
/*!
@@ -1244,14 +1244,14 @@ void QDomNodeListPrivate::createList()
QDomNodePrivate* QDomNodeListPrivate::item(int index)
{
if (!node_impl)
- return 0;
+ return nullptr;
const QDomDocumentPrivate *const doc = node_impl->ownerDocument();
if (!doc || timestamp != doc->nodeListTime)
createList();
if (index >= list.size())
- return 0;
+ return nullptr;
return list.at(index);
}
@@ -1305,13 +1305,13 @@ int QDomNodeListPrivate::length() const
Creates an empty node list.
*/
QDomNodeList::QDomNodeList()
+ : impl(nullptr)
{
- impl = 0;
}
QDomNodeList::QDomNodeList(QDomNodeListPrivate* p)
+ : impl(p)
{
- impl = p;
}
/*!
@@ -1443,10 +1443,10 @@ QDomNodePrivate::QDomNodePrivate(QDomDocumentPrivate *doc, QDomNodePrivate *par)
setParent(par);
else
setOwnerDocument(doc);
- prev = 0;
- next = 0;
- first = 0;
- last = 0;
+ prev = nullptr;
+ next = nullptr;
+ first = nullptr;
+ last = nullptr;
createdWithDom1Interface = true;
lineNumber = -1;
columnNumber = -1;
@@ -1455,10 +1455,10 @@ QDomNodePrivate::QDomNodePrivate(QDomDocumentPrivate *doc, QDomNodePrivate *par)
QDomNodePrivate::QDomNodePrivate(QDomNodePrivate *n, bool deep) : ref(1)
{
setOwnerDocument(n->ownerDocument());
- prev = 0;
- next = 0;
- first = 0;
- last = 0;
+ prev = nullptr;
+ next = nullptr;
+ first = nullptr;
+ last = nullptr;
name = n->name;
value = n->value;
@@ -1488,8 +1488,8 @@ QDomNodePrivate::~QDomNodePrivate()
p->setNoParent();
p = n;
}
- first = 0;
- last = 0;
+ first = nullptr;
+ last = nullptr;
}
void QDomNodePrivate::clear()
@@ -1503,8 +1503,8 @@ void QDomNodePrivate::clear()
delete p;
p = n;
}
- first = 0;
- last = 0;
+ first = nullptr;
+ last = nullptr;
}
QDomNodePrivate* QDomNodePrivate::namedItem(const QString &n)
@@ -1515,7 +1515,7 @@ QDomNodePrivate* QDomNodePrivate::namedItem(const QString &n)
return p;
p = p->next;
}
- return 0;
+ return nullptr;
}
@@ -1523,15 +1523,15 @@ QDomNodePrivate* QDomNodePrivate::insertBefore(QDomNodePrivate* newChild, QDomNo
{
// Error check
if (!newChild)
- return 0;
+ return nullptr;
// Error check
if (newChild == refChild)
- return 0;
+ return nullptr;
// Error check
if (refChild && refChild->parent() != this)
- return 0;
+ return nullptr;
// "mark lists as dirty"
QDomDocumentPrivate *const doc = ownerDocument();
@@ -1542,7 +1542,7 @@ QDomNodePrivate* QDomNodePrivate::insertBefore(QDomNodePrivate* newChild, QDomNo
// all elements of the fragment instead of the fragment itself.
if (newChild->isDocumentFragment()) {
// Fragment is empty ?
- if (newChild->first == 0)
+ if (newChild->first == nullptr)
return newChild;
// New parent
@@ -1553,7 +1553,7 @@ QDomNodePrivate* QDomNodePrivate::insertBefore(QDomNodePrivate* newChild, QDomNo
}
// Insert at the beginning ?
- if (!refChild || refChild->prev == 0) {
+ if (!refChild || refChild->prev == nullptr) {
if (first)
first->prev = newChild->last;
newChild->last->next = first;
@@ -1572,8 +1572,8 @@ QDomNodePrivate* QDomNodePrivate::insertBefore(QDomNodePrivate* newChild, QDomNo
// does not decrease the reference.
// Remove the nodes from the fragment
- newChild->first = 0;
- newChild->last = 0;
+ newChild->first = nullptr;
+ newChild->last = nullptr;
return newChild;
}
@@ -1596,7 +1596,7 @@ QDomNodePrivate* QDomNodePrivate::insertBefore(QDomNodePrivate* newChild, QDomNo
return newChild;
}
- if (refChild->prev == 0) {
+ if (refChild->prev == nullptr) {
if (first)
first->prev = newChild;
newChild->next = first;
@@ -1618,15 +1618,15 @@ QDomNodePrivate* QDomNodePrivate::insertAfter(QDomNodePrivate* newChild, QDomNod
{
// Error check
if (!newChild)
- return 0;
+ return nullptr;
// Error check
if (newChild == refChild)
- return 0;
+ return nullptr;
// Error check
if (refChild && refChild->parent() != this)
- return 0;
+ return nullptr;
// "mark lists as dirty"
QDomDocumentPrivate *const doc = ownerDocument();
@@ -1637,7 +1637,7 @@ QDomNodePrivate* QDomNodePrivate::insertAfter(QDomNodePrivate* newChild, QDomNod
// all elements of the fragment instead of the fragment itself.
if (newChild->isDocumentFragment()) {
// Fragment is empty ?
- if (newChild->first == 0)
+ if (newChild->first == nullptr)
return newChild;
// New parent
@@ -1648,7 +1648,7 @@ QDomNodePrivate* QDomNodePrivate::insertAfter(QDomNodePrivate* newChild, QDomNod
}
// Insert at the end
- if (!refChild || refChild->next == 0) {
+ if (!refChild || refChild->next == nullptr) {
if (last)
last->next = newChild->first;
newChild->first->prev = last;
@@ -1666,8 +1666,8 @@ QDomNodePrivate* QDomNodePrivate::insertAfter(QDomNodePrivate* newChild, QDomNod
// does not decrease the reference.
// Remove the nodes from the fragment
- newChild->first = 0;
- newChild->last = 0;
+ newChild->first = nullptr;
+ newChild->last = nullptr;
return newChild;
}
@@ -1692,7 +1692,7 @@ QDomNodePrivate* QDomNodePrivate::insertAfter(QDomNodePrivate* newChild, QDomNod
return newChild;
}
- if (refChild->next == 0) {
+ if (refChild->next == nullptr) {
if (last)
last->next = newChild;
newChild->prev = last;
@@ -1713,11 +1713,11 @@ QDomNodePrivate* QDomNodePrivate::insertAfter(QDomNodePrivate* newChild, QDomNod
QDomNodePrivate* QDomNodePrivate::replaceChild(QDomNodePrivate* newChild, QDomNodePrivate* oldChild)
{
if (!newChild || !oldChild)
- return 0;
+ return nullptr;
if (oldChild->parent() != this)
- return 0;
+ return nullptr;
if (newChild == oldChild)
- return 0;
+ return nullptr;
// mark lists as dirty
QDomDocumentPrivate *const doc = ownerDocument();
@@ -1728,7 +1728,7 @@ QDomNodePrivate* QDomNodePrivate::replaceChild(QDomNodePrivate* newChild, QDomNo
// all elements of the fragment instead of the fragment itself.
if (newChild->isDocumentFragment()) {
// Fragment is empty ?
- if (newChild->first == 0)
+ if (newChild->first == nullptr)
return newChild;
// New parent
@@ -1753,15 +1753,15 @@ QDomNodePrivate* QDomNodePrivate::replaceChild(QDomNodePrivate* newChild, QDomNo
last = newChild->last;
oldChild->setNoParent();
- oldChild->next = 0;
- oldChild->prev = 0;
+ oldChild->next = nullptr;
+ oldChild->prev = nullptr;
// No need to increase the reference since QDomDocumentFragment
// does not decrease the reference.
// Remove the nodes from the fragment
- newChild->first = 0;
- newChild->last = 0;
+ newChild->first = nullptr;
+ newChild->last = nullptr;
// We are no longer interested in the old node
if (oldChild)
@@ -1794,8 +1794,8 @@ QDomNodePrivate* QDomNodePrivate::replaceChild(QDomNodePrivate* newChild, QDomNo
last = newChild;
oldChild->setNoParent();
- oldChild->next = 0;
- oldChild->prev = 0;
+ oldChild->next = nullptr;
+ oldChild->prev = nullptr;
// We are no longer interested in the old node
if (oldChild)
@@ -1808,7 +1808,7 @@ QDomNodePrivate* QDomNodePrivate::removeChild(QDomNodePrivate* oldChild)
{
// Error check
if (oldChild->parent() != this)
- return 0;
+ return nullptr;
// "mark lists as dirty"
QDomDocumentPrivate *const doc = ownerDocument();
@@ -1817,8 +1817,8 @@ QDomNodePrivate* QDomNodePrivate::removeChild(QDomNodePrivate* oldChild)
// Perhaps oldChild was just created with "createElement" or that. In this case
// its parent is QDomDocument but it is not part of the documents child list.
- if (oldChild->next == 0 && oldChild->prev == 0 && first != oldChild)
- return 0;
+ if (oldChild->next == nullptr && oldChild->prev == nullptr && first != oldChild)
+ return nullptr;
if (oldChild->next)
oldChild->next->prev = oldChild->prev;
@@ -1831,8 +1831,8 @@ QDomNodePrivate* QDomNodePrivate::removeChild(QDomNodePrivate* oldChild)
first = oldChild->next;
oldChild->setNoParent();
- oldChild->next = 0;
- oldChild->prev = 0;
+ oldChild->next = nullptr;
+ oldChild->prev = nullptr;
// We are no longer interested in the old node
oldChild->ref.deref();
@@ -1843,7 +1843,7 @@ QDomNodePrivate* QDomNodePrivate::removeChild(QDomNodePrivate* oldChild)
QDomNodePrivate* QDomNodePrivate::appendChild(QDomNodePrivate* newChild)
{
// No reference manipulation needed. Done in insertAfter.
- return insertAfter(newChild, 0);
+ return insertAfter(newChild, nullptr);
}
QDomDocumentPrivate* QDomNodePrivate::ownerDocument()
@@ -1869,7 +1869,7 @@ QDomNodePrivate* QDomNodePrivate::cloneNode(bool deep)
static void qNormalizeNode(QDomNodePrivate* n)
{
QDomNodePrivate* p = n->first;
- QDomTextPrivate* t = 0;
+ QDomTextPrivate* t = nullptr;
while (p) {
if (p->isText()) {
@@ -1884,7 +1884,7 @@ static void qNormalizeNode(QDomNodePrivate* n)
}
} else {
p = p->next;
- t = 0;
+ t = nullptr;
}
}
}
@@ -2009,8 +2009,8 @@ void QDomNodePrivate::setLocation(int lineNumber, int columnNumber)
Constructs a \l{isNull()}{null} node.
*/
QDomNode::QDomNode()
+ : impl(nullptr)
{
- impl = 0;
}
/*!
@@ -2619,7 +2619,7 @@ bool QDomNode::hasChildNodes() const
{
if (!impl)
return false;
- return IMPL->first != 0;
+ return IMPL->first != nullptr;
}
/*!
@@ -2628,7 +2628,7 @@ bool QDomNode::hasChildNodes() const
*/
bool QDomNode::isNull() const
{
- return (impl == 0);
+ return (impl == nullptr);
}
/*!
@@ -2641,7 +2641,7 @@ void QDomNode::clear()
{
if (impl && !impl->ref.deref())
delete impl;
- impl = 0;
+ impl = nullptr;
}
/*!
@@ -3094,13 +3094,13 @@ QDomNodePrivate* QDomNamedNodeMapPrivate::namedItemNS(const QString& nsURI, cons
return n;
}
}
- return 0;
+ return nullptr;
}
QDomNodePrivate* QDomNamedNodeMapPrivate::setNamedItem(QDomNodePrivate* arg)
{
if (readonly || !arg)
- return 0;
+ return nullptr;
if (appendToParent)
return parent->appendChild(arg);
@@ -3115,7 +3115,7 @@ QDomNodePrivate* QDomNamedNodeMapPrivate::setNamedItem(QDomNodePrivate* arg)
QDomNodePrivate* QDomNamedNodeMapPrivate::setNamedItemNS(QDomNodePrivate* arg)
{
if (readonly || !arg)
- return 0;
+ return nullptr;
if (appendToParent)
return parent->appendChild(arg);
@@ -3136,11 +3136,11 @@ QDomNodePrivate* QDomNamedNodeMapPrivate::setNamedItemNS(QDomNodePrivate* arg)
QDomNodePrivate* QDomNamedNodeMapPrivate::removeNamedItem(const QString& name)
{
if (readonly)
- return 0;
+ return nullptr;
QDomNodePrivate* p = namedItem(name);
- if (p == 0)
- return 0;
+ if (p == nullptr)
+ return nullptr;
if (appendToParent)
return parent->removeChild(p);
@@ -3153,7 +3153,7 @@ QDomNodePrivate* QDomNamedNodeMapPrivate::removeNamedItem(const QString& name)
QDomNodePrivate* QDomNamedNodeMapPrivate::item(int index) const
{
if (index >= length() || index < 0)
- return 0;
+ return nullptr;
return *(map.constBegin() + index);
}
@@ -3164,12 +3164,12 @@ int QDomNamedNodeMapPrivate::length() const
bool QDomNamedNodeMapPrivate::contains(const QString& name) const
{
- return map.value(name) != 0;
+ return map.contains(name);
}
bool QDomNamedNodeMapPrivate::containsNS(const QString& nsURI, const QString & localName) const
{
- return namedItemNS(nsURI, localName) != 0;
+ return namedItemNS(nsURI, localName) != nullptr;
}
/**************************************************************
@@ -3222,8 +3222,8 @@ bool QDomNamedNodeMapPrivate::containsNS(const QString& nsURI, const QString & l
Constructs an empty named node map.
*/
QDomNamedNodeMap::QDomNamedNodeMap()
+ : impl(nullptr)
{
- impl = 0;
}
/*!
@@ -3570,7 +3570,7 @@ QDomNodePrivate* QDomDocumentTypePrivate::removeChild(QDomNodePrivate* oldChild)
QDomNodePrivate* QDomDocumentTypePrivate::appendChild(QDomNodePrivate* newChild)
{
- return insertAfter(newChild, 0);
+ return insertAfter(newChild, nullptr);
}
static QString quotedValue(const QString &data)
@@ -4115,7 +4115,7 @@ QDomAttrPrivate::QDomAttrPrivate(QDomAttrPrivate* n, bool deep)
void QDomAttrPrivate::setNodeValue(const QString& v)
{
value = v;
- QDomTextPrivate *t = new QDomTextPrivate(0, this, v);
+ QDomTextPrivate *t = new QDomTextPrivate(nullptr, this, v);
// keep the refcount balanced: appendChild() does a ref anyway.
t->ref.deref();
if (first) {
@@ -4489,7 +4489,7 @@ void QDomElementPrivate::setAttributeNS(const QString& nsURI, const QString& qNa
void QDomElementPrivate::removeAttribute(const QString& aname)
{
QDomNodePrivate* p = m_attr->removeNamedItem(aname);
- if (p && p->ref.load() == 0)
+ if (p && p->ref.loadRelaxed() == 0)
delete p;
}
@@ -4517,7 +4517,7 @@ QDomAttrPrivate* QDomElementPrivate::setAttributeNode(QDomAttrPrivate* newAttr)
QDomAttrPrivate* QDomElementPrivate::setAttributeNodeNS(QDomAttrPrivate* newAttr)
{
- QDomNodePrivate* n = 0;
+ QDomNodePrivate* n = nullptr;
if (!newAttr->prefix.isNull())
n = m_attr->namedItemNS(newAttr->namespaceURI, newAttr->name);
@@ -5184,10 +5184,10 @@ QDomTextPrivate* QDomTextPrivate::splitText(int offset)
{
if (!parent()) {
qWarning("QDomText::splitText The node has no parent. So I cannot split");
- return 0;
+ return nullptr;
}
- QDomTextPrivate* t = new QDomTextPrivate(ownerDocument(), 0, value.mid(offset));
+ QDomTextPrivate* t = new QDomTextPrivate(ownerDocument(), nullptr, value.mid(offset));
value.truncate(offset);
parent()->insertAfter(t, this);
@@ -6144,7 +6144,7 @@ void QDomProcessingInstruction::setData(const QString& d)
**************************************************************/
QDomDocumentPrivate::QDomDocumentPrivate()
- : QDomNodePrivate(0),
+ : QDomNodePrivate(nullptr),
impl(new QDomImplementationPrivate),
nodeListTime(1)
{
@@ -6155,7 +6155,7 @@ QDomDocumentPrivate::QDomDocumentPrivate()
}
QDomDocumentPrivate::QDomDocumentPrivate(const QString& aname)
- : QDomNodePrivate(0),
+ : QDomNodePrivate(nullptr),
impl(new QDomImplementationPrivate),
nodeListTime(1)
{
@@ -6167,11 +6167,11 @@ QDomDocumentPrivate::QDomDocumentPrivate(const QString& aname)
}
QDomDocumentPrivate::QDomDocumentPrivate(QDomDocumentTypePrivate* dt)
- : QDomNodePrivate(0),
+ : QDomNodePrivate(nullptr),
impl(new QDomImplementationPrivate),
nodeListTime(1)
{
- if (dt != 0) {
+ if (dt != nullptr) {
type = dt;
} else {
type = new QDomDocumentTypePrivate(this, this);
@@ -6267,9 +6267,9 @@ QDomElementPrivate* QDomDocumentPrivate::createElement(const QString &tagName)
bool ok;
QString fixedName = fixedXmlName(tagName, &ok);
if (!ok)
- return 0;
+ return nullptr;
- QDomElementPrivate *e = new QDomElementPrivate(this, 0, fixedName);
+ QDomElementPrivate *e = new QDomElementPrivate(this, nullptr, fixedName);
e->ref.deref();
return e;
}
@@ -6279,16 +6279,16 @@ QDomElementPrivate* QDomDocumentPrivate::createElementNS(const QString &nsURI, c
bool ok;
QString fixedName = fixedXmlName(qName, &ok, true);
if (!ok)
- return 0;
+ return nullptr;
- QDomElementPrivate *e = new QDomElementPrivate(this, 0, nsURI, fixedName);
+ QDomElementPrivate *e = new QDomElementPrivate(this, nullptr, nsURI, fixedName);
e->ref.deref();
return e;
}
QDomDocumentFragmentPrivate* QDomDocumentPrivate::createDocumentFragment()
{
- QDomDocumentFragmentPrivate *f = new QDomDocumentFragmentPrivate(this, (QDomNodePrivate*)0);
+ QDomDocumentFragmentPrivate *f = new QDomDocumentFragmentPrivate(this, (QDomNodePrivate*)nullptr);
f->ref.deref();
return f;
}
@@ -6298,9 +6298,9 @@ QDomTextPrivate* QDomDocumentPrivate::createTextNode(const QString &data)
bool ok;
QString fixedData = fixedCharData(data, &ok);
if (!ok)
- return 0;
+ return nullptr;
- QDomTextPrivate *t = new QDomTextPrivate(this, 0, fixedData);
+ QDomTextPrivate *t = new QDomTextPrivate(this, nullptr, fixedData);
t->ref.deref();
return t;
}
@@ -6310,9 +6310,9 @@ QDomCommentPrivate* QDomDocumentPrivate::createComment(const QString &data)
bool ok;
QString fixedData = fixedComment(data, &ok);
if (!ok)
- return 0;
+ return nullptr;
- QDomCommentPrivate *c = new QDomCommentPrivate(this, 0, fixedData);
+ QDomCommentPrivate *c = new QDomCommentPrivate(this, nullptr, fixedData);
c->ref.deref();
return c;
}
@@ -6322,9 +6322,9 @@ QDomCDATASectionPrivate* QDomDocumentPrivate::createCDATASection(const QString &
bool ok;
QString fixedData = fixedCDataSection(data, &ok);
if (!ok)
- return 0;
+ return nullptr;
- QDomCDATASectionPrivate *c = new QDomCDATASectionPrivate(this, 0, fixedData);
+ QDomCDATASectionPrivate *c = new QDomCDATASectionPrivate(this, nullptr, fixedData);
c->ref.deref();
return c;
}
@@ -6335,13 +6335,13 @@ QDomProcessingInstructionPrivate* QDomDocumentPrivate::createProcessingInstructi
bool ok;
QString fixedData = fixedPIData(data, &ok);
if (!ok)
- return 0;
+ return nullptr;
// [17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l'))
QString fixedTarget = fixedXmlName(target, &ok);
if (!ok)
- return 0;
+ return nullptr;
- QDomProcessingInstructionPrivate *p = new QDomProcessingInstructionPrivate(this, 0, fixedTarget, fixedData);
+ QDomProcessingInstructionPrivate *p = new QDomProcessingInstructionPrivate(this, nullptr, fixedTarget, fixedData);
p->ref.deref();
return p;
}
@@ -6350,9 +6350,9 @@ QDomAttrPrivate* QDomDocumentPrivate::createAttribute(const QString &aname)
bool ok;
QString fixedName = fixedXmlName(aname, &ok);
if (!ok)
- return 0;
+ return nullptr;
- QDomAttrPrivate *a = new QDomAttrPrivate(this, 0, fixedName);
+ QDomAttrPrivate *a = new QDomAttrPrivate(this, nullptr, fixedName);
a->ref.deref();
return a;
}
@@ -6362,9 +6362,9 @@ QDomAttrPrivate* QDomDocumentPrivate::createAttributeNS(const QString &nsURI, co
bool ok;
QString fixedName = fixedXmlName(qName, &ok, true);
if (!ok)
- return 0;
+ return nullptr;
- QDomAttrPrivate *a = new QDomAttrPrivate(this, 0, nsURI, fixedName);
+ QDomAttrPrivate *a = new QDomAttrPrivate(this, nullptr, nsURI, fixedName);
a->ref.deref();
return a;
}
@@ -6374,16 +6374,16 @@ QDomEntityReferencePrivate* QDomDocumentPrivate::createEntityReference(const QSt
bool ok;
QString fixedName = fixedXmlName(aname, &ok);
if (!ok)
- return 0;
+ return nullptr;
- QDomEntityReferencePrivate *e = new QDomEntityReferencePrivate(this, 0, fixedName);
+ QDomEntityReferencePrivate *e = new QDomEntityReferencePrivate(this, nullptr, fixedName);
e->ref.deref();
return e;
}
QDomNodePrivate* QDomDocumentPrivate::importNode(QDomNodePrivate *importedNode, bool deep)
{
- QDomNodePrivate *node = 0;
+ QDomNodePrivate *node = nullptr;
switch (importedNode->nodeType()) {
case QDomNode::AttributeNode:
node = new QDomAttrPrivate((QDomAttrPrivate*)importedNode, true);
@@ -6435,7 +6435,7 @@ void QDomDocumentPrivate::saveDocument(QTextStream& s, const int indent, QDomNod
#if QT_CONFIG(textcodec) && QT_CONFIG(regularexpression)
const QDomNodePrivate* n = first;
- QTextCodec *codec = 0;
+ QTextCodec *codec = nullptr;
if (n && n->isProcessingInstruction() && n->nodeName() == QLatin1String("xml")) {
// we have an XML declaration
@@ -6593,7 +6593,7 @@ void QDomDocumentPrivate::saveDocument(QTextStream& s, const int indent, QDomNod
*/
QDomDocument::QDomDocument()
{
- impl = 0;
+ impl = nullptr;
}
/*!
@@ -6822,7 +6822,7 @@ bool QDomDocument::setContent(QXmlInputSource *source, QXmlReader *reader, QStri
{
if (!impl)
impl = new QDomDocumentPrivate();
- return IMPL->setContent(source, reader, 0, errorMsg, errorLine, errorColumn);
+ return IMPL->setContent(source, reader, nullptr, errorMsg, errorLine, errorColumn);
}
/*!
@@ -7369,7 +7369,7 @@ QDomComment QDomNode::toComment() const
QDomHandler::QDomHandler(QDomDocumentPrivate* adoc, QXmlSimpleReader* areader, bool namespaceProcessing)
: errorLine(0), errorColumn(0), doc(adoc), node(adoc), cdata(false),
- nsProcessing(namespaceProcessing), locator(0), reader(areader)
+ nsProcessing(namespaceProcessing), locator(nullptr), reader(areader)
{
}
@@ -7443,7 +7443,7 @@ bool QDomHandler::characters(const QString& ch)
if (cdata) {
n.reset(doc->createCDATASection(ch));
} else if (!entityName.isEmpty()) {
- QScopedPointer<QDomEntityPrivate> e(new QDomEntityPrivate(doc, 0, entityName,
+ QScopedPointer<QDomEntityPrivate> e(new QDomEntityPrivate(doc, nullptr, entityName,
QString(), QString(), QString()));
e->value = ch;
e->ref.deref();
@@ -7528,7 +7528,7 @@ bool QDomHandler::comment(const QString& ch)
bool QDomHandler::unparsedEntityDecl(const QString &name, const QString &publicId, const QString &systemId, const QString &notationName)
{
- QDomEntityPrivate* e = new QDomEntityPrivate(doc, 0, name,
+ QDomEntityPrivate* e = new QDomEntityPrivate(doc, nullptr, name,
publicId, systemId, notationName);
// keep the refcount balanced: appendChild() does a ref anyway.
e->ref.deref();
@@ -7543,7 +7543,7 @@ bool QDomHandler::externalEntityDecl(const QString &name, const QString &publicI
bool QDomHandler::notationDecl(const QString & name, const QString & publicId, const QString & systemId)
{
- QDomNotationPrivate* n = new QDomNotationPrivate(doc, 0, name, publicId, systemId);
+ QDomNotationPrivate* n = new QDomNotationPrivate(doc, nullptr, name, publicId, systemId);
// keep the refcount balanced: appendChild() does a ref anyway.
n->ref.deref();
doc->doctype()->appendChild(n);
diff --git a/src/xml/sax/qxml.cpp b/src/xml/sax/qxml.cpp
index b2fff5b61f..1c45118fb1 100644
--- a/src/xml/sax/qxml.cpp
+++ b/src/xml/sax/qxml.cpp
@@ -1079,12 +1079,12 @@ void QXmlInputSource::init()
d = new QXmlInputSourcePrivate;
QT_TRY {
- d->inputDevice = 0;
- d->inputStream = 0;
+ d->inputDevice = nullptr;
+ d->inputStream = nullptr;
setData(QString());
#if QT_CONFIG(textcodec)
- d->encMapper = 0;
+ d->encMapper = nullptr;
#endif
d->nextReturnedEndOfData = true; // first call to next() will call fetchData()
@@ -1357,13 +1357,13 @@ QString QXmlInputSource::fromRawData(const QByteArray &data, bool beginning)
return QString();
if (beginning) {
delete d->encMapper;
- d->encMapper = 0;
+ d->encMapper = nullptr;
}
int mib = 106; // UTF-8
// This is the initial UTF codec we will read the encoding declaration with
- if (d->encMapper == 0) {
+ if (d->encMapper == nullptr) {
d->encodingDeclBytes.clear();
d->encodingDeclChars.clear();
d->lookingForEncodingDecl = true;
@@ -2377,7 +2377,7 @@ bool QXmlDefaultHandler::unparsedEntityDecl(const QString&, const QString&,
bool QXmlDefaultHandler::resolveEntity(const QString&, const QString&,
QXmlInputSource*& ret)
{
- ret = 0;
+ ret = nullptr;
return true;
}
@@ -2520,15 +2520,15 @@ inline void QXmlSimpleReaderPrivate::refClear()
QXmlSimpleReaderPrivate::QXmlSimpleReaderPrivate(QXmlSimpleReader *reader)
{
q_ptr = reader;
- parseStack = 0;
+ parseStack = nullptr;
locator.reset(new QXmlSimpleReaderLocator(reader));
- entityRes = 0;
- dtdHnd = 0;
- contentHnd = 0;
- errorHnd = 0;
- lexicalHnd = 0;
- declHnd = 0;
+ entityRes = nullptr;
+ dtdHnd = nullptr;
+ contentHnd = nullptr;
+ errorHnd = nullptr;
+ lexicalHnd = nullptr;
+ declHnd = nullptr;
// default feature settings
useNamespaces = true;
@@ -2932,7 +2932,7 @@ bool QXmlSimpleReader::feature(const QString& name, bool *ok) const
{
const QXmlSimpleReaderPrivate *d = d_func();
- if (ok != 0)
+ if (ok)
*ok = true;
if (name == QLatin1String("http://xml.org/sax/features/namespaces")) {
return d->useNamespaces;
@@ -2946,7 +2946,7 @@ bool QXmlSimpleReader::feature(const QString& name, bool *ok) const
return d->reportEntities;
} else {
qWarning("Unknown feature %s", name.toLatin1().data());
- if (ok != 0)
+ if (ok)
*ok = false;
}
return false;
@@ -3023,9 +3023,9 @@ bool QXmlSimpleReader::hasFeature(const QString& name) const
*/
void* QXmlSimpleReader::property(const QString&, bool *ok) const
{
- if (ok != 0)
+ if (ok)
*ok = false;
- return 0;
+ return nullptr;
}
/*! \reimp
@@ -3206,7 +3206,7 @@ bool QXmlSimpleReader::parse(const QXmlInputSource *input, bool incremental)
d->initIncrementalParsing();
} else {
delete d->parseStack;
- d->parseStack = 0;
+ d->parseStack = nullptr;
}
d->init(input);
@@ -3251,7 +3251,7 @@ bool QXmlSimpleReader::parse(const QXmlInputSource *input, bool incremental)
bool QXmlSimpleReader::parseContinue()
{
Q_D(QXmlSimpleReader);
- if (d->parseStack == 0 || d->parseStack->isEmpty())
+ if (d->parseStack == nullptr || d->parseStack->isEmpty())
return false;
d->initData();
int state = d->parseStack->pop().state;
@@ -3268,7 +3268,7 @@ bool QXmlSimpleReaderPrivate::parseBeginOrContinue(int state, bool incremental)
if (state==0) {
if (!parseProlog()) {
if (incremental && error.isNull()) {
- pushParseState(0, 0);
+ pushParseState(nullptr, 0);
return true;
} else {
clear(tags);
@@ -3280,7 +3280,7 @@ bool QXmlSimpleReaderPrivate::parseBeginOrContinue(int state, bool incremental)
if (state==1) {
if (!parseElement()) {
if (incremental && error.isNull()) {
- pushParseState(0, 1);
+ pushParseState(nullptr, 1);
return true;
} else {
clear(tags);
@@ -3293,7 +3293,7 @@ bool QXmlSimpleReaderPrivate::parseBeginOrContinue(int state, bool incremental)
while (!atEnd()) {
if (!parseMisc()) {
if (incremental && error.isNull()) {
- pushParseState(0, 2);
+ pushParseState(nullptr, 2);
return true;
} else {
clear(tags);
@@ -3303,7 +3303,7 @@ bool QXmlSimpleReaderPrivate::parseBeginOrContinue(int state, bool incremental)
}
if (!atEndOrig && incremental) {
// we parsed something at all, so be prepared to come back later
- pushParseState(0, 2);
+ pushParseState(nullptr, 2);
return true;
}
// is stack empty?
@@ -3315,7 +3315,7 @@ bool QXmlSimpleReaderPrivate::parseBeginOrContinue(int state, bool incremental)
// call the handler
if (contentHnd) {
delete parseStack;
- parseStack = 0;
+ parseStack = nullptr;
if (!contentHnd->endDocument()) {
reportParseError(contentHnd->errorString());
return false;
@@ -3350,7 +3350,7 @@ bool QXmlSimpleReaderPrivate::parseBeginOrContinue(int state, bool incremental)
signed char state;
signed char input;
-(4) if (d->parseStack == 0 || d->parseStack->isEmpty()) {
+(4) if (d->parseStack == nullptr || d->parseStack->isEmpty()) {
(4a) ...
} else {
(4b) ...
@@ -3440,7 +3440,7 @@ bool QXmlSimpleReaderPrivate::parseProlog()
signed char state;
signed char input;
- if (parseStack == 0 || parseStack->isEmpty()) {
+ if (parseStack == nullptr|| parseStack->isEmpty()) {
xmldecl_possible = true;
doctype_read = false;
state = Init;
@@ -3631,7 +3631,7 @@ bool QXmlSimpleReaderPrivate::parseElement()
int state;
int input;
- if (parseStack == 0 || parseStack->isEmpty()) {
+ if (parseStack == nullptr|| parseStack->isEmpty()) {
state = Init;
} else {
state = parseStack->pop().state;
@@ -4000,7 +4000,7 @@ bool QXmlSimpleReaderPrivate::parseContent()
signed char state;
signed char input;
- if (parseStack == 0 || parseStack->isEmpty()) {
+ if (parseStack == nullptr || parseStack->isEmpty()) {
contentCharDataRead = false;
state = Init;
} else {
@@ -4303,7 +4303,7 @@ bool QXmlSimpleReaderPrivate::parseMisc()
signed char state;
signed char input;
- if (parseStack==0 || parseStack->isEmpty()) {
+ if (parseStack == nullptr || parseStack->isEmpty()) {
state = Init;
} else {
state = parseStack->pop().state;
@@ -4458,7 +4458,7 @@ bool QXmlSimpleReaderPrivate::parsePI()
signed char state;
signed char input;
- if (parseStack==0 || parseStack->isEmpty()) {
+ if (parseStack == nullptr || parseStack->isEmpty()) {
state = Init;
} else {
state = parseStack->pop().state;
@@ -4685,7 +4685,7 @@ bool QXmlSimpleReaderPrivate::parseDoctype()
signed char state;
signed char input;
- if (parseStack==0 || parseStack->isEmpty()) {
+ if (parseStack == nullptr || parseStack->isEmpty()) {
startDTDwasReported = false;
systemId.clear();
publicId.clear();
@@ -4896,7 +4896,7 @@ bool QXmlSimpleReaderPrivate::parseExternalID()
signed char state;
signed char input;
- if (parseStack==0 || parseStack->isEmpty()) {
+ if (parseStack == nullptr || parseStack->isEmpty()) {
systemId.clear();
publicId.clear();
state = Init;
@@ -5060,7 +5060,7 @@ bool QXmlSimpleReaderPrivate::parseMarkupdecl()
signed char state;
signed char input;
- if (parseStack==0 || parseStack->isEmpty()) {
+ if (parseStack == nullptr || parseStack->isEmpty()) {
state = Init;
} else {
state = parseStack->pop().state;
@@ -5218,7 +5218,7 @@ bool QXmlSimpleReaderPrivate::parsePEReference()
signed char state;
signed char input;
- if (parseStack==0 || parseStack->isEmpty()) {
+ if (parseStack == nullptr || parseStack->isEmpty()) {
state = Init;
} else {
state = parseStack->pop().state;
@@ -5255,7 +5255,7 @@ bool QXmlSimpleReaderPrivate::parsePEReference()
} else if (entityRes) {
QMap<QString,QXmlSimpleReaderPrivate::ExternParameterEntity>::Iterator it2;
it2 = externParameterEntities.find(ref());
- QXmlInputSource *ret = 0;
+ QXmlInputSource *ret = nullptr;
if (it2 != externParameterEntities.end()) {
if (!entityRes->resolveEntity((*it2).publicId, (*it2).systemId, ret)) {
delete ret;
@@ -5396,7 +5396,7 @@ bool QXmlSimpleReaderPrivate::parseAttlistDecl()
signed char state;
signed char input;
- if (parseStack==0 || parseStack->isEmpty()) {
+ if (parseStack == nullptr || parseStack->isEmpty()) {
state = Init;
} else {
state = parseStack->pop().state;
@@ -5612,7 +5612,7 @@ bool QXmlSimpleReaderPrivate::parseAttType()
signed char state;
signed char input;
- if (parseStack==0 || parseStack->isEmpty()) {
+ if (parseStack == nullptr || parseStack->isEmpty()) {
state = Init;
} else {
state = parseStack->pop().state;
@@ -5833,7 +5833,7 @@ bool QXmlSimpleReaderPrivate::parseAttValue()
signed char state;
signed char input;
- if (parseStack==0 || parseStack->isEmpty()) {
+ if (parseStack == nullptr || parseStack->isEmpty()) {
state = Init;
} else {
state = parseStack->pop().state;
@@ -5975,7 +5975,7 @@ bool QXmlSimpleReaderPrivate::parseElementDecl()
signed char state;
signed char input;
- if (parseStack==0 || parseStack->isEmpty()) {
+ if (parseStack == nullptr || parseStack->isEmpty()) {
state = Init;
} else {
state = parseStack->pop().state;
@@ -6184,7 +6184,7 @@ bool QXmlSimpleReaderPrivate::parseNotationDecl()
signed char state;
signed char input;
- if (parseStack==0 || parseStack->isEmpty()) {
+ if (parseStack == nullptr || parseStack->isEmpty()) {
state = Init;
} else {
state = parseStack->pop().state;
@@ -6328,7 +6328,7 @@ bool QXmlSimpleReaderPrivate::parseChoiceSeq()
signed char state;
signed char input;
- if (parseStack==0 || parseStack->isEmpty()) {
+ if (parseStack == nullptr || parseStack->isEmpty()) {
state = Init;
} else {
state = parseStack->pop().state;
@@ -6557,7 +6557,7 @@ bool QXmlSimpleReaderPrivate::parseEntityDecl()
signed char state;
signed char input;
- if (parseStack==0 || parseStack->isEmpty()) {
+ if (parseStack == nullptr || parseStack->isEmpty()) {
state = Init;
} else {
state = parseStack->pop().state;
@@ -6832,7 +6832,7 @@ bool QXmlSimpleReaderPrivate::parseEntityValue()
signed char state;
signed char input;
- if (parseStack==0 || parseStack->isEmpty()) {
+ if (parseStack == nullptr || parseStack->isEmpty()) {
state = Init;
} else {
state = parseStack->pop().state;
@@ -6951,7 +6951,7 @@ bool QXmlSimpleReaderPrivate::parseComment()
signed char state;
signed char input;
- if (parseStack==0 || parseStack->isEmpty()) {
+ if (parseStack == nullptr || parseStack->isEmpty()) {
state = Init;
} else {
state = parseStack->pop().state;
@@ -7063,7 +7063,7 @@ bool QXmlSimpleReaderPrivate::parseAttribute()
int state;
int input;
- if (parseStack==0 || parseStack->isEmpty()) {
+ if (parseStack == nullptr || parseStack->isEmpty()) {
state = Init;
} else {
state = parseStack->pop().state;
@@ -7162,7 +7162,7 @@ bool QXmlSimpleReaderPrivate::parseName()
};
int state;
- if (parseStack==0 || parseStack->isEmpty()) {
+ if (parseStack == nullptr || parseStack->isEmpty()) {
state = Init;
} else {
state = parseStack->pop().state;
@@ -7248,7 +7248,7 @@ bool QXmlSimpleReaderPrivate::parseNmtoken()
signed char state;
signed char input;
- if (parseStack==0 || parseStack->isEmpty()) {
+ if (parseStack == nullptr || parseStack->isEmpty()) {
state = Init;
} else {
state = parseStack->pop().state;
@@ -7356,7 +7356,7 @@ bool QXmlSimpleReaderPrivate::parseReference()
signed char state;
signed char input;
- if (parseStack==0 || parseStack->isEmpty()) {
+ if (parseStack == nullptr || parseStack->isEmpty()) {
parseReference_charDataRead = false;
state = Init;
} else {
@@ -7582,7 +7582,7 @@ bool QXmlSimpleReaderPrivate::processReference()
if (parseReference_context == InContent) {
if (contentCharDataRead) {
if (reportWhitespaceCharData || !string().simplified().isEmpty()) {
- if (contentHnd != 0 && !contentHnd->characters(string())) {
+ if (contentHnd != nullptr && !contentHnd->characters(string())) {
reportParseError(contentHnd->errorString());
return false;
}
@@ -7610,7 +7610,7 @@ bool QXmlSimpleReaderPrivate::processReference()
// Included if validating
bool skipIt = true;
if (entityRes) {
- QXmlInputSource *ret = 0;
+ QXmlInputSource *ret = nullptr;
if (!entityRes->resolveEntity((*itExtern).publicId, (*itExtern).systemId, ret)) {
delete ret;
reportParseError(entityRes->errorString());
@@ -7696,7 +7696,7 @@ bool QXmlSimpleReaderPrivate::parseString()
signed char state; // state in this function is the position in the string s
signed char input;
- if (parseStack==0 || parseStack->isEmpty()) {
+ if (parseStack == nullptr || parseStack->isEmpty()) {
Done = parseString_s.length();
state = 0;
} else {
@@ -7800,7 +7800,7 @@ void QXmlSimpleReaderPrivate::next()
c = inputSource->next();
// If we are not incremental parsing, we just skip over EndOfData chars to give the
// parser an uninterrupted stream of document chars.
- if (c == QXmlInputSource::EndOfData && parseStack == 0)
+ if (c == QXmlInputSource::EndOfData && parseStack == nullptr)
c = inputSource->next();
if (uc == '\n') {
lineNr++;
@@ -7832,7 +7832,7 @@ bool QXmlSimpleReaderPrivate::eat_ws()
}
next();
}
- if (parseStack != 0) {
+ if (parseStack != nullptr) {
unexpectedEof(&QXmlSimpleReaderPrivate::eat_ws, 0);
return false;
}
@@ -7922,7 +7922,7 @@ void QXmlSimpleReaderPrivate::reportParseError(const QString& error)
*/
void QXmlSimpleReaderPrivate::unexpectedEof(ParseFunction where, int state)
{
- if (parseStack == 0) {
+ if (parseStack == nullptr) {
reportParseError(QLatin1String(XMLERR_UNEXPECTEDEOF));
} else {
if (c == QXmlInputSource::EndOfDocument) {
@@ -7942,7 +7942,7 @@ void QXmlSimpleReaderPrivate::unexpectedEof(ParseFunction where, int state)
*/
void QXmlSimpleReaderPrivate::parseFailed(ParseFunction where, int state)
{
- if (parseStack!=0 && error.isNull()) {
+ if (parseStack != nullptr && error.isNull()) {
pushParseState(where, state);
}
}